I'm building a Streamlit multipage application and am having trouble keeping session state when switching between pages. My main page is called mainpage.py and has something like the following:
import streamlit as st
if "multi_select" not in st.session_state:
st.session_state["multi_select"] = ["abc", "xyz"]
if "select_slider" not in st.session_state:
st.session_state["select_slider"] = ("1", "10")
if "text_inp" not in st.session_state:
st.session_state["text_inp"] = ""
st.sidebar.multiselect(
"multiselect",
["abc", "xyz"],
key="multi_select",
default=st.session_state["multi_select"],
)
st.sidebar.select_slider(
"number range",
options=[str(n) for n in range(1, 11)],
key="select_slider",
value=st.session_state["select_slider"],
)
st.sidebar.text_input("Text:", key="text_inp")
for v in st.session_state:
st.write(v, st.session_state[v])
Next, I have another page called 'anotherpage.py' in a subdirectory called 'pages' with this content:
import streamlit as st
for v in st.session_state:
st.write(v, st.session_state[v])
If I run this app, change the values of the controls and switch to the other page, I see the values for the control being retained and printed. However, if I switch back to the main page, everything gets reset to the original values. For some reason st.session_state
is cleared.
Anyone have any idea how to keep the values in the session state? I'm using Python 3.11.1
and Streamlit 1.16.0
First, it's important to understand a widget's lifecycle. When you assign a key to a widget, then that key will get deleted from session state whenever that widget is not rendered. This can happen if a widget is conditionally not rendered on the same page or from switching pages.
What you are seeing on the second page are the values leftover from the previous page before the widget cleanup process is completed. At the end of loading "anotherpage," Streamlit realizes it has keys in session state assigned to widgets that have disappeared and therefore deletes them.
There are two ways around this.
st.session_state.my_widget_key = st.session_state.my_widget_key
This will interrupt the widget cleanup process and prevent the keys from being deleted. However, it needs to be on the page you go to when leaving a widget. Hence, it needs to be on all the pages.
import streamlit as st
if "multi_select" not in st.session_state:
st.session_state["multi_select"] = ["abc", "xyz"]
if "select_slider" not in st.session_state:
st.session_state["select_slider"] = ("1","10")
if "text_inp" not in st.session_state:
st.session_state["text_inp"] = ""
def keep(key):
# Copy from temporary widget key to permanent key
st.session_state[key] = st.session_state['_'+key]
def unkeep(key):
# Copy from permanent key to temporary widget key
st.session_state['_'+key] = st.session_state[key]
unkeep("multi_select")
st.sidebar.multiselect(
"multiselect",
["abc", "xyz"],
key="_multi_select",
on_change=keep,
args=['multi_select']
)
# This is a edge case and possibly a bug. See explanation.
st.sidebar.select_slider(
"number range",
options=[str(n) for n in range(1, 11)],
value = st.session_state.select_slider,
key="_select_slider",
on_change=keep,
args=["select_slider"]
)
unkeep("text_inp")
st.sidebar.text_input("Text:", key="_text_inp", on_change=keep, args=["text_inp"])
for v in st.session_state:
st.write(v, st.session_state[v])
You will observe I did something different with the select slider. It appears a tuple needs to be passed to the value
kwarg specifically to make sure it initializes as a ranged slider. I wouldn't have needed to change the logic if it was being initialized with a single value instead of a ranged value. For other widgets, you can see that the default value is removed in favor of directly controlling their value via their key in session state.
You need to be careful when you do something that changes a widget's default value. A change to the default value creates a "new widget." If you are simultaneously changing the default value and actual value via its key, you can get some nuanced behavior like initialization warnings if there is ever a conflict.