enabling interactive elements after disabling them

Hey, thanks for your thoughtful question and for sharing your code! :balloon: You’re right: simply setting st.session_state.disabled = False at the end of your process won’t immediately re-enable widgets, because Streamlit only updates widget states on rerun. Using st.rerun() after your process does reset the UI, but it also clears transient outputs like st.toast() since the script restarts from the top and doesn’t “remember” the toast.

The recommended approach is to save any outputs you want to persist (like a “done” message) in st.session_state, then display them after rerun. For example, set a flag like st.session_state.show_done = True before calling st.rerun(), and then conditionally show the toast at the top of your script if that flag is set. This way, the UI updates and your message persists for at least one rerun cycle. See the code below for a pattern that works well:

import streamlit as st
import time

if "disabled" not in st.session_state:
    st.session_state.disabled = False
if "run_requested" not in st.session_state:
    st.session_state.run_requested = False
if "show_done" not in st.session_state:
    st.session_state.show_done = False

def start_run():
    st.session_state.disabled = True
    st.session_state.run_requested = True

st.button(
    "Run process",
    disabled=st.session_state.disabled,
    on_click=start_run
)

if st.session_state.run_requested:
    # Simulate long process
    time.sleep(10)
    st.session_state.run_requested = False
    st.session_state.disabled = False
    st.session_state.show_done = True
    st.rerun()

if st.session_state.show_done:
    st.toast("done")
    st.session_state.show_done = False

This pattern ensures widgets are disabled during processing, re-enabled after, and your “done” message appears after rerun. For more details and variations, see this forum thread and this related discussion. If you run into issues, please share a minimum reproducible example and let the community jump in!

Sources: