Streamlit how to display buttons in a single line
Asked Answered
H

4

16

Hi all I am building a simple web app with streamlit in python. I need to add 3 buttons but they must be on the same line.

Obviously the following code puts them on three different lines

st.button('Button 1')
st.button('Button 2')
st.button('Button 3')

Do you have any tips?

Hu answered 8/10, 2021 at 7:56 Comment(1)
You can accept my answer or you can build your own Streamlit component with JS. streamlit.io/components Please remember this is a framework for data-science, not for front-end pixel perfection as Flask could be for example.Baucom
H
30

Apparently this should do it

col1, col2, col3 = st.columns([1,1,1])

with col1:
    st.button('1')
with col2:
    st.button('2')
with col3:
    st.button('3')
Hu answered 8/10, 2021 at 8:12 Comment(1)
To get the buttons to be closer together, experiment with columns = st.columns([1, 1, 1, 1, 1]) (or some other number of 1s) and then doing with columns[0] etc.Parttime
S
6

If you're like me, it might bother you that the columns equally space, making layout not visually appealing because the buttons appear far apart.

After a lot of CSS digging, I found a pretty simple approach that doesn't require any other library installs. It is possible (at least for the current version of streamlit) to apply the following CSS to make columns only as wide as the button. I included this at the top of my file:

st.markdown("""
            <style>
                div[data-testid="column"] {
                    width: fit-content !important;
                    flex: unset;
                }
                div[data-testid="column"] * {
                    width: fit-content !important;
                }
            </style>
            """, unsafe_allow_html=True)

And then (for my use case):

col1, col2, col3 = st.columns([1,1,1])
    with col1:
        st.button(...)
    with col2:
        st.button(...)
    with col3:
        st.download_button(...)
Serotine answered 20/10, 2023 at 15:28 Comment(6)
This still works with streamlit==1.27.2 and looks a lot nicer than mucking around with relative col widths.Florenceflorencia
Variable length columns are very easy to make. Just say: first, second, third, _ = st.columns[0.1, 0.2, 0.2, 0.5] Now in this setup, the first, second and third columns will be within 50% of the width. First column would occup 10%, rest two will occupy 20% Now, you can create any width you like. Its simplePantsuit
Hi @JigidiSarnath , I agreed that this is also a good solution. The approach above is intended to automatically make columns the exact width of your buttons. If an app will always be viewed on one screen size, your solution would likely be best as it doesn't require CSS hacks.Serotine
Ah! I am not a CSS guy so obviously I didnt understand the neatness in your solution. Great to know! Thanks!Pantsuit
I think this affects the format whenever I use st.column() in the page. Is there a way to do this only for buttons?Isisiskenderun
@ckcn, unfortunately not that I could think of - If you create a multi-page app and only use the above on one page, the columns on other pages will not be affected. It's unideal, but that's the closest I got to a full work-aroundSerotine
R
3

I had a similar problem - to add an action button to a table. I came to the following approach:

import streamlit as st

        # # Show users table 
        colms = st.columns((1, 2, 2, 1, 1))
        fields = ["№", 'email', 'uid', 'verified', "action"]
        for col, field_name in zip(colms, fields):
            # header
            col.write(field_name)

        for x, email in enumerate(user_table['email']):
            col1, col2, col3, col4, col5 = st.columns((1, 2, 2, 1, 1))
            col1.write(x)  # index
            col2.write(user_table['email'][x])  # email
            col3.write(user_table['uid'][x])  # unique ID
            col4.write(user_table['verified'][x])   # email status
            disable_status = user_table['disabled'][x]  # flexible type of button
            button_type = "Unblock" if disable_status else "Block"
            button_phold = col5.empty()  # create a placeholder
            do_action = button_phold.button(button_type, key=x)
            if do_action:
                 pass # do some action with row's data
                 button_phold.empty()  #  remove button

And it works fine. The object — user_table — is a dictionary very similar to DataFrame, where each key — is a column (i.e. list in pythonic terms). And here how it looks like (Note “Blocked” — that is the result of action): enter image description here

Rachelrachele answered 12/12, 2021 at 1:1 Comment(1)
Love it! Solved my problem! This is what I was looking for. I always used to create columns only one and then reuse it. but creating column for every row... may actually work and align better I think. I am yet to try. You saved my day today! Thank you!Pantsuit
T
2

Generalizing this answer a bit to use a dynamic number of buttons:

import streamlit as st  # 1.18.1


button_text = "foo", "bar", "baz"

for text, col in zip(button_text, st.columns(len(button_text))):
    if col.button(text):
        col.write(f"{text} clicked")

If the text isn't necessarily unique:

button_text = "foo", "bar", "foo"
pairs = zip(button_text, st.columns(len(button_text)))

for i, (text, col) in enumerate(pairs):
    if col.button(text, key=f"{text}-{i}"):
        col.write(f"{text}-{i} clicked")
Task answered 16/2, 2023 at 20:0 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.