Avoiding refreshing the entire page when clicking a button

Hi there,

Have been using Streamlit for a few weeks and am really loving it.

Iā€™ve built an app that performs 10-20s of computation, displaying output as it goes. Once it comes to the end, I would like it to ask the user if they want to see additional information.

The idea is that this would be a button which, upon being clicked, would proceed to load more information and display it. Think something like this:

st.write("Doing stuff on every page load")
...
st.write("Finished doing stuff")

if st.button("Do you want to see more?"):
    st.write("Doing more optional stuff")

My issue right now is that when the user presses the button, the entire page reloads. I understand why this is the case given Streamlitā€™s execution flow, and I understand why it makes sense.

My question is if thereā€™s a way around this? I have seen sporadic mentions of the session_state but I donā€™t see how that answer applies to this case, since Iā€™m not interested in saving information between runs. Sure, I could memoize the entire rest of the page but thatā€™s not ideal either, as Iā€™d have to make an artbitrary decision on the TTL. Also, there are definitely things in the app that cannot be pickled right now.

Thank you in advance for any help!

Douglas

Hi @Doggie52,

First, welcome to the Streamlit community! :tada: :hugs: :star: :partying_face:

Although session state seems like it might not apply in this case it does. What you want to do is use the button to add or change a value that you hold in state, and you will use this value to display your optional information!!! :partying_face:

(another work around for this is actually the expander, I will mock up an example of each)

import streamlit as st

col1,buff, col2 =st.columns([2,.3,2])
with col1:
    st.subheader("Expander example")

    st.write("""Doing stuff on every page load

...

Finished doing stuff""")

    with st.expander("Do you want to see more?"):
        st.write("Doing more optional stuff")

with col2:
    if "more_stuff" not in st.session_state:
        st.session_state.more_stuff = False

    st.subheader("Button example")

    st.write("""Doing stuff on every page load

...

Finished doing stuff""")


    click = st.button("Do you want to see more?")
    if click:
        st.session_state.more_stuff = True

    if st.session_state.more_stuff:
        st.write("Doing more optional stuff")

this gives your two options that look like this:

Happy Streamlit-ing!
Marisa

1 Like

Hi Marisa - thanks for getting back to me. Unfortunately, your example isnā€™t a solution to my problem - I took your code, added a spinner with a sleep to it at the top of the page and found that clicking the ā€œDo you want to see more?ā€ button did indeed re-trigger that spinner.

The expander isnā€™t a solution either, since that requires the content within the expander to be run regardless of whether youā€™ve expanded it or not.

Hi @Doggie52,

So, let me be clear, the Streamlit execution flow will always work like this. Each time you interact with a widget on the page the entire Streamlit script will be re-run from top to bottom.

The button solution I provided allows you to ā€œhideā€ additional widgets behind a button click. Normally, when you have a button it returns True on only one run-through of the app and then changes back to its default (False). But the method I coded for you above, allows you to link a value in st.session_state that you can use instead of the return value of the button itself to run additional ā€œstuffā€ in your app that will persist past the buttonā€™s natural true-only-once behaviour.

To get this method execution to work with your spinner you will likely need to add some logic to your spinner for various conditions for your app. But I wonā€™t be able to code any examples for you until I see a minimum working example of the execution flow youā€™re trying to achieve.

Happy Streamlit-ing!
Marisa

1 Like

Thanks for getting back to me. As you indicate, I think it isnā€™t possible to work around this behaviour in Streamlit right now.

Hey @Doggie52,

you can probably solve this with caching.

But could you give me a few more details about what you are doing in your app (i.e. the ā€œ10-20s of computation, displaying output as it goesā€)? We are currently looking at how we can improve caching, so this would be really useful feedback for me!! If you canā€™t share publicly, we can also write via DM or jump on a short call if you like :slight_smile:

Cheers, Johannes

Hey there - apologies for a belated reply. Happy to jump on a call - love the product so am happy to offer any input. Will DM you!

Is there any solution for this issue now??

Re-running the application on each button click is pretty much unavoidable. If that is making your application slow, the solution is caching, either using cache primitives or session_state.

1 Like

Well, this example worked for me:

Hi! Iā€™m also running into this exact same issue & would love help!

Iā€™m working on an app where a user inputs a prompt:
the response dataframe is displayed, and I want them to be able to edit this dataframe response using st.dataeditor, and the edited dataframe is stored as a variable and then the same function continues with the userā€™s edited dataframe as a part of its new information.
Then, it returns another ā€˜in between stepā€™ dataframe that the user can edit, that is again used in the rest of the function, until it finally creates the final, downloadable CSV for the user!

That sounds extremely vague so Iā€™m also happy to DM or hop on a quick call if this hasnā€™t been figured out yet. Or happy to hear any advice if anyone has it.

Thank you for all your time and help in making streamlit, Iā€™m such a huge fan streamlit is literally my favourite thing on the web right now. Iā€™m making so many useful apps with it.

Also, the notion pages on this website are not public (or maybe its just me haha in which case I am so sorry but it keeps saying I donā€™t have access when I login with my notion) just in case theres more documentation there.

Editable dataframes - Spec](https://www.notion.so/streamlit/Spec-v2-0eb61c2fbb954b2dac9f7c0d7cf96169)
Adding and deleting rows - Spec](https://www.notion.so/streamlit/Spec-5765492b85cb46f6af3146dba21111df)

downloadable CSV for the user

I tried to use caching but it did not work. My app reads a CSV input, and returns a CSV output.

@st.cache_data(persist=True)
        def convert_df(df):
            # IMPORTANT: Cache the conversion to prevent computation on every rerun
            return df.to_csv().encode('utf-8')
        csv = convert_df(df)

I would appreciate any help, Iā€™ve been working on this for like a week and Iā€™ve got to present it in a few days