Hi
I am trying to use st.button but its behavior is puzzling me. Check the code below:
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
print("Disabled run_state",st.session_state.run_state)
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")
if st.button("Reset Button" ):
disable(False)
When I execute this code and click the “Say hello” button, the button is disabled due to the on_click function.
However, when I click the “Reset Button”, the st.session_state.run_state value changes to False, but the “Say hello” button still remains disabled. I need to click the “Reset Button” twice to enable the “Say hello” button.
Why is it behaving this way? I don’t want to click the reset button twice.
Am I doing something wrong, or is this the expected behavior.
Please explain.
Thanks.
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:
When I run this code, I get the following message:
Calling st.rerun() within a callback is a no-op.
This code does not work in streamlit version 1.61 and above.
When the button “say hello” is clicked, it disables but does not return a True value.
If used without the disabled parameter, the button works.
It does however work in version 1.55 but I am not sure till which number.
I think this is a bug.
Can you please fix?
Thanks