St.button behavior

Welcome to the Streamlit community, and thanks for your thoughtful question! :balloon: What you’re seeing is a common gotcha with Streamlit’s session state and widget rendering order: when you update st.session_state.run_state after the “Say hello” button is already rendered, the change doesn’t affect the current run—only the next one. That’s why you need to click “Reset Button” twice: the first click updates the state, and the second rerun actually enables the button.

To fix this, update the session state before rendering the button, or use a callback with st.button that sets the state and triggers a rerun. For example, you can use st.rerun() after resetting the state so the UI reflects the change immediately. See the Streamlit docs on button/session state behavior and forum discussions for more details.

Here’s a pattern that works:

import streamlit as st

if "run_state" not in st.session_state:
    st.session_state.run_state = False

def disable(b):
    st.session_state["run_state"] = b

def reset():
    st.session_state["run_state"] = False
    st.rerun()

if st.button("Say hello", disabled=st.session_state.run_state, on_click=disable, args=(True,)):
    st.write("Why hello there")
else:
    st.write("Goodbye")

st.button("Reset Button", on_click=reset)

Sources: