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

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

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!