Streamlit Session State Management using st.session_state to efficiently handle dynamic user inputs and maintain the application's state across reruns.
Step 1: Setup and State Initialization
This step initializes the application and ensures the necessary keys are present in st.session_state before they are accessed. This pattern prevents KeyError on the first run.
# Step 1: Setup and State Initialization
import streamlit as st
import time
# --- State Initialization Pattern ---
# Check if the keys exist in session_state; if not, initialize them.
if 'page_count' not in st.session_state:
st.session_state.page_count = 1 # Tracks a conceptual 'page' or step
st.session_state.user_name = "Guest" # Stores persistent user input
st.session_state.processed_data = [] # Stores results of an action
st.session_state.button_clicked = False # Stores status of a button
# --- Streamlit App Layout ---
st.title("Streamlit Session State Demo")
st.markdown("Session State persists inputs and variables across reruns.")
st.header(f"Current State: Page {st.session_state.page_count}")
st.write(f"Hello, **{st.session_state.user_name}**!")
Step 2: Handling User Input and Persistence
This step demonstrates how to use st.session_state to handle the values of input widgets (st.text_input). By setting the key parameter on the widget, Streamlit automatically manages the variable in the Session State.
# Step 2: Handling User Input and Persistence
st.subheader("Persistent User Input")
# Use 'st.text_input' and assign a key.
# Streamlit automatically stores its value in st.session_state[key].
# The 'value' field is set to read from the session state upon rerun.
user_input = st.text_input(
"Enter your name (persists on rerun):",
value=st.session_state.user_name, # Initialize with current state
key='user_name' # Links widget value to st.session_state.user_name
)
st.write(f"Input Widget Value: **{user_input}**")
st.write(f"Session State Value: **{st.session_state.user_name}**")
# NOTE: The values are identical because the widget is linked by 'key'.
# If the user types a new name, st.session_state.user_name is updated
# automatically, and the app reruns.
Step 3: Managing Dynamic Actions and Page Flow
This step shows how Session State can be used to manage application flow (like moving between pages) and store the results of actions (like processing data after a button click) without losing data on subsequent reruns.
# Step 3: Managing Dynamic Actions and Page Flow
st.subheader("Dynamic Action Management")
def update_state_and_process():
"""Callback function to run when the button is clicked."""
st.session_state.button_clicked = True
# Simulate a time-consuming data processing task
st.session_state.processed_data = [
f"Data item for {st.session_state.user_name} - {i}" for i in range(5)
]
st.session_state.page_count += 1
st.info("Data processed and state updated! App will now rerun.")
# Button to trigger the update_state_and_process function
st.button(
"Process Data & Go to Next Step",
on_click=update_state_and_process,
disabled=st.session_state.button_clicked # Disable after first click
)
# --- Display Results Based on State ---
if st.session_state.button_clicked:
st.success(f"Processing Complete (Step {st.session_state.page_count - 1} data):")
for item in st.session_state.processed_data:
st.code(item)
# Example of another button to reset the state
if st.button("Reset Application"):
# Reset relevant state variables to initial values
st.session_state.page_count = 1
st.session_state.processed_data = []
st.session_state.button_clicked = False
st.rerun() # Force a full rerun to reflect the reset state
Step 4: Conditional Rendering Based on State
This step demonstrates using the Session State to control which components are visible to the user, creating a multi-step or multi-page experience without using actual Streamlit pages.
# Step 4: Conditional Rendering Based on State (Multi-Step Flow)
st.subheader("State-Driven UI Flow")
# Use st.session_state.page_count to determine what to show
if st.session_state.page_count == 1:
st.info("👋 Welcome! Please enter your name and process the data to continue.")
# All previous widgets (Input, Process button) are visible here.
elif st.session_state.page_count == 2:
st.success("🎉 You are now on Step 2.")
st.write(f"Showing results for **{st.session_state.user_name}**:")
# Only display the processed data and the Reset button from Step 3 here.
# The initial input and process button are now conceptually hidden
# (or placed in an 'if page_count == 1' block in a real app).
# Display the stored data from the previous step
for item in st.session_state.processed_data:
st.markdown(f"- *{item}*")
if st.button("Go Back to Step 1"):
# Explicitly set the state to return to the first step
st.session_state.page_count = 1
st.session_state.button_clicked = False
st.rerun()
else:
# Handle unexpected page counts
st.error("Error: Application is in an invalid state. Please reset.")
Step 5: Using State for Interactive Filtering
This final step shows a practical use of Session State: maintaining filter selections when the underlying data visualization or table rerenders. This is crucial for interactive data applications.
# Step 5: Using State for Interactive Filtering
st.subheader("State for Data Filtering")
# --- Initialize Filter State ---
if 'filter_value' not in st.session_state:
st.session_state.filter_value = 0.5
# --- Sample Data (must be defined outside the conditional block) ---
@st.cache_data
def load_sample_data():
return pd.DataFrame({
'data_id': range(1, 11),
'value': np.linspace(0, 1, 10),
'category': np.random.choice(['A', 'B', 'C'], 10)
})
sample_df = load_sample_data()
# --- Filter Widget ---
st.write("Filter data based on 'value':")
new_filter_value = st.slider(
"Minimum Value Threshold",
0.0, 1.0,
value=st.session_state.filter_value,
step=0.1,
key='filter_slider' # Links widget value to st.session_state.filter_value
)
# --- Apply Filter and Display ---
filtered_df = sample_df[sample_df['value'] >= st.session_state.filter_value]
st.dataframe(filtered_df, use_container_width=True)
st.info(f"Displaying {len(filtered_df)} out of {len(sample_df)} rows (Filtered by: value >= {st.session_state.filter_value})")
# KEY CONCEPT: Even if other parts of the app cause a rerun, the slider's
# position (st.session_state.filter_value) is preserved, ensuring a consistent
# user experience.
