Problem
Streamlit operates on a top-down execution model where every widget interaction triggers an app rerun. This occurs whenever user interacts with widget elements such as filling out text/number input box, adjusting values in a slider widget, selecting values in select boxes, etc.
Solution
Here are some ways to prevent this.
1. Use st.form
Embed your widgets in st.form
and upon providing the necessary input, users can proceed further by clicking on st.form_submit_button
. This simple approach resolves the app rerun issue with every widget interaction as it requires the user to explicitly click on the button to process the widget values.
Consider the following simple example from this blog:
with st.form(key='my_form'):
text_input = st.text_input(label='Enter some text')
submit_button = st.form_submit_button(label='Submit')
2. Session state
As widget interaction triggers a rerun, but with session state it is possible to have values persist across reruns for instances when you don’t want your variables reinitialized.
In essence, session state variables can be created (if it does not exist) and updated (so that its value is retained after the rerun).
Consider a simple counter example:
# Initialize session state variables
if 'count' not in st.session_state:
st.session_state.count = 0
# Update session state variables
if st.button('Increment'):
st.session_state.count += 1
if st.button('Decrement'):
st.session_state.count -= 1
# Print session state variable
st.write('Count = ', st.session_state.count)
3. Session state with Callback functions
We can also have widget interactions handled by callback functions. Particularly, when a widget is linked to a callback function, any modification to the widget initiates the following sequence: Firstly, the callback function is executed, next the app runs sequentially from top to bottom.
# Initialize session state variables
if 'count_value' not in st.session_state:
st.session_state.count_value = 0
# Callback functions
def increment_counter():
st.session_state.count_value += 1
def decrement_counter():
st.session_state.count_value -= 1
st.button('Increment', on_click=increment_counter, key='increment_btn')
st.button('Decrement', on_click=decrement_counter, key='decrement_btn')
# Print session state variable
st.write('Count = ', st.session_state.count_value)
Note:
For all code snippet examples, it is implied that you are implementing the code in a Streamlit app and thus have the necessary import statements in place (e.g.import streamlit as st
).
Resources
Demo app that demonstrate examples described herein:
- App: https://faq-session-state.streamlit.app/
- GitHub repo: GitHub - sfc-gh-cnantasenamat/st-faq-session-state
Streamlit Docs:
YouTube tutorials:
- Session State basics
- How to use Streamlit session states and callback functions | Make your apps remember things!
- Streamlit Tricks - Web App Reruns on every Widget Clicks ? Here’s What-to-do |SessionState| Python
- How to make NESTED buttons in Streamlit with Session State
Forum posts:
Blog posts: