Widgets
Widgets (like st.button, st.selectbox, and st.text_input) are at the heart of Streamlit apps. They are the interactive elements of Streamlit that pass information from your users into your Python code.
Session dependent
Widget states are dependent on a particular session (browser connection). The actions of one user do not affect the widgets of another user. Furthermore, if a user opens up multiple tabs to access an app, each tab will be a unique session. Changing a widget in one tab will not affect the same widget in another tab.
Return type
The values returned by a widget function call are of simple Python types. For example, st.button returns a boolean value and will have the same boolean value saved in st.session_state if using a key.
Default value
NOTE
Default values are configurable for all widgets with a few special exceptions like st.button and st.file_uploader.
The first time a widget function is called (before a user interacts with it), it will return its default value (e.g. st.selectbox returns the first option by default).
Widget keys
Widget keys serve two purposes:
- Distinguishing two otherwise identical widgets.
- Creating a means to access and manipulate the widget's value through
st.session_state.
Streamlit assigns a unique ID to each widget based on parameters such as label, min or max value, default value, placeholder text, help text, and key. The page where the widget appears also factors into a widget's ID.
If you have two widgets of the same type with the same arguments on the same page, you will get a DuplicateWidgetID error. To address this, use different values for the widget keys.
# This will cause a DuplicateWidgetID error.
st.button("OK")
st.button("OK")
# This will not cause any error.
st.button("OK", key="privacy")
st.button("OK", key="terms")Order of operations
When a user interacts with a widget, the sequence of operations is as follows:
- Its value in
st.session_stateis updated. - The callback function (if any) is executed.
- The page reruns with the widget function returning its new value.
Because of this, you can't pass the widget's new value in its own callback function. Instead, you should use st.session_state with the widget and pass the state instead.
Also, because callback function is executed before page rerun, anything written by callback function will appear above the rest of the page. That is, a callback function runs as a prefix to the script rerunning.
Widget life cycle
Streamlit assigns a unique ID to each widget based on parameters such as label, min or max value, default value, placeholder text, help text, key and the page where it's rendered. Changing any of these will reset the widget and a new widget is created on rerun.
Note that, callback functions, callback args and kwargs, label visibility and disabling a widget DO NOT affect a widget's identity.
Widget doesn't already exist on rerun
If your script rerun calls a widget function with changed parameters or calls a widget function that wasn't used on the last script run:
- Streamlit will build the frontend and backend parts of the widget, using its default value.
- If the widget has been assigned a key, Streamlit will check if that key already exists in Session State.
- If it exists and is not currently associated with another widget, Streamlit will assign that key's value to the widget.
- Otherwise, it will assign the default value to the key in
st.session_state(creating a new key-value pair or overwriting an existing one). - If there are args or kwargs for a callback function, they are computed and saved at this point in time.
- The widget value is then returned by the function.
Widget already exists on rerun
When rerunning a script without changing widget parameters (or rather its ID):
- Streamlit will connect to the existing frontend and backend parts.
- If the widget has a key that was deleted from
st.session_state, then Streamlit will recreate the key using the current frontend value. (e.g Deleting a key will not revert the widget to a default value.) - It will return the current value of the widget.
WARNING
If a widget attaches to a pre-existing key when created and is also manually assigned a default value, you will get a warning if there is a disparity. So, in order to control a widget's value through st.session_state, initialize the widget's value through st.session_state and avoid the default value argument to prevent conflict.
Statefulness of widgets
As long as the defining parameters of a widget remain the same and that widget is continuously rendered on the frontend, then it will be stateful and remember user input.
Changing parameters will reset widgets
As mentioned above, Streamlit assigns a new ID when the parameters of the widget change. So, upon app reruns, the widget will then reset to default value. In fact, the widget will be deleted and re-created as a new one, as explained below in widget clean-up process.
Suppose you have a widget like this:
st.number_input("Alpha",key="A")and you change it on a page rerun to:
st.number_input("Beta",key="A")Because the label is changed, Streamlit will see it as new widget and so it deletes the widget labeled Alpha and it's state and it will recreate the state with default value to use with widget labeled Beta.
Widget clean-up
If a widget function is not called during a script run, it will be deleted from memory. In other words, if a widget ID from the memory is not rendered on the screen, it will be destroyed. More importantly, Streamlit will also delete all key-value pairs in st.session_state associated with a widget not currently on screen. This is the widget clean-up process, which is executed at the end of the script run.
Preserve widget state between pages
Because the page where the widget is rendered contributes to widget's ID, changing the page in a Multipage app, will reset it due to the widget clean-up process (the new page will definitely not have the same widget ID, since the page changed). But if you want to preserve widget state between pages, you can do so by saving the state to a different key in Session state.
import streamlit as st
def store_value():
# Copy the value to the permanent key
st.session_state["my_key"] = st.session_state["_my_key"]
# Copy the saved value to the temporary key
st.session_state["_my_key"] = st.session_state["my_key"]
st.number_input("Number of filters", key="_my_key", on_change=store_value)Interrupt widget clean-up
The other way to preserve widget state across pages is to interrupt widget clean-up process. You can do so, by adding the following line to the top of each page:
if "my_key" in st.session_state:
st.session_state.my_key = st.session_state.my_keyIf you navigate away from a widget with some key
my_keyand save data tost.session_state.my_keyon the new page, you will interrupt the widget clean-up process and prevent the key-value pair from being deleted or overwritten.
Retain statefulness when changing widget parameters
You can interrupt widget clean-up even on the same page when a widget parameter changes. Here is a example for it:
import streamlit as st
# Set default value
if "a" not in st.session_state:
st.session_state.a = 5
# Inputs to set min and max values for the slider widget
cols = st.columns(2)
minimum = cols[0].number_input("Min", 1, 5, key="min")
maximum = cols[1].number_input("Max", 6, 10, 10, key="max")
def update_value():
# Helper function to ensure consistency between widget parameters and value
st.session_state.a = min(st.session_state.a, maximum)
st.session_state.a = max(st.session_state.a, minimum)
# Validate the slider value before rendering
update_value()
st.slider("A", minimum, maximum, key="a")