Button inside a dataframe

Hi! Everyone, Is there a way to insert a button (not checkbox) inside a dataframe. So that for each row we have a separate button and when clicked we can perform some action.

1 Like

Hello Vedant :wave:,

As far as I know, you can’t directly add a button inside a (native) dataframe – There may be a hack but I’m not aware of it! :smile:

Some questions:

  • Why wouldn’t the checkbox be a viable option?
  • Have you also tried other options such as streamlit-aggrid?

Thanks,
Charly

I’m facing the same issue, I have a table where one column needs to redirect to a ā€˜details’ page with that row’s info. I want to use st.page_link to go to the details page since it preserves st.session_state and is generally easy to use, or add a st.button with a st.switch_page callback. A link column doesn’t solve my problem because I would need to point to ā€œhttp://localhost:port/ā€ during development and change the link for a deploy, and to my knowledge there is no way to get the current url in streamlit (that doesn’t use JS or an equally hacky solution). Honestly, a generic callback option when clicking a table cell would be the best and easiest (I think) solution. I’ll try to use streamlit-aggrid but it would be better to have a native option for streamlit.

You can use a checkbox instead of a button. With 1.35, it has a feature to select a single row, column, etc.

Here is a sample demo.

image


After pressing the page link.

image


streamlit_app.py

import streamlit as st
import pandas as pd


data = {
    'Country': ['United States', 'Canada', 'Germany', 'France', 'Japan'],
    'Capital': ['Washington, D.C.', 'Ottawa', 'Berlin', 'Paris', 'Tokyo']
}


df = pd.DataFrame(data)
st.title('Countries and Their Capitals')

event = st.dataframe(
    df,
    on_select='rerun',
    selection_mode='single-row'
)

if len(event.selection['rows']):
    selected_row = event.selection['rows'][0]
    country = df.iloc[selected_row]['Country']
    capital = df.iloc[selected_row]['Capital']

    st.session_state['country_data'] = {'country': country, 'capital': capital}
    st.page_link('pages/detail.py', label=f'Goto {country} Page', icon='šŸ—ŗļø')

pages/detail.py

import streamlit as st


st.set_page_config(layout='centered')


st.subheader('Detail Page')
st.write(st.session_state['country_data'])
1 Like

Thanks for the code snippet, @ferdy! :hugs:

I actually used checkboxes (as in a boolean column in an editable table) to solve this issue before the selection feature was available, but I don’t think this is the best solution yet.

My main issue with checkboxes is that from a design pov they imply that more than one option can be selected, which is not the desired behavior. A radio button would be better, but then I would need another button to confirm the user’s selection, which is an unneeded button press. The best solution, in my opinion, still is a button row.

I’ll look into the select row and column feature as soon as I have some time, maybe there’s a way to achieve this behavior without using checkboxes.

1 Like

That belief is incorrect. Have a look on this discussion at ux stack.

One nice feature of this table is the ability to clear the selection.

The link you presented reinforces what I said. Checkboxes imply zero or more selections, but in my use case only one option should be selected, which triggers a redirect to another page. Radio buttons would be more appropriate since options are mutually exclusive, but it still feels strange clicking a radio button to go to another page.

You are incorrect again. You said

Would like to see a solution as well… Aggrid is cool, but I want to use actual streamlit buttons, not HTML rendered ones

1 Like

Same here - I’m building an overview table, and each row should redirect to the (dynamically generated) details page of this row’s entry.

My work-around is:

  • Configure the streamlit dataframe with selection_mode="single-row"
  • Define a callback that is executed every time the row selection changes
def single_row_selection_callback():
    # note: it is not possible to have st.switch_page in the callback
    st.session_state["mp_single_row_selection_callback"] = True


event = st.dataframe(
    data,
    on_select=single_row_selection_callback,
    selection_mode="single-row",
)

modules = event.selection.rows  

if st.session_state.get("mp_single_row_selection_callback", False) is True:
    st.session_state["mp_single_row_selection_callback"] = False
    st.session_state["mp_selected_uid"] = int(data.loc[modules[0]]["uid"]) if len(modules) > 0 else None
    if st.session_state["mp_selected_uid"] is not None:
        st.cache_data.clear()  # clear the cache and force the module details page to refresh
        st.switch_page("pages/5_module_details.py")

The very first column can be used to select an entry (a ā€œmoduleā€ in my case):


selecting one of the rows via the first column will cause the script to switch to a new page with details about the selected entry.

For now this is good enough, but it would be much nicer to be able to click on a cell, e.g. UID or Name and have the script switch to the corresponding details page.

1 Like

Currently, there’s an open issue requesting this feature. It is useful for when you need to execute functions relevant to a row.

Under the hood Streamlit uses the Glide data grid for the dataframes, it seems it supports buttons as well, so integration to Streamlit should be possible.

I’d be willing to contribute, but adding this functionality seems quite involved as, looking at the other column types, many files need to be modified/added.

I’ve been looking at NiceGUI, which seems to have more features and freedom compared to Streamlit, to transfer my app so I could get buttons-in-rows feature. However, with NiceGUI it’s literally hacking a custom language blob into Python script to make it happen… it looks the part, but unmaintainable and code is ugly.

For now the best solution for me, using Streamlit, remains using columns with st.button().

I developed a component for this, feel free to check this out.

1 Like