Welcome to the Streamlit community, and thanks for your thoughtful question!
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: