backend / web frameworks / streamlit / 02_session_state.md

Streamlit Session State - Interview Questions

3 interview angles 5 min read source

Streamlit Session State - Interview Questions

1. What is st.session_state and why do you need it?

Streamlit re-runs the script top-to-bottom on every interaction. Local variables are reset each run. st.session_state is a dict-like object that persists across reruns within the same browser session.

import streamlit as st

if "count" not in st.session_state:
    st.session_state["count"] = 0

if st.button("Increment"):
    st.session_state["count"] += 1

st.write("Count:", st.session_state["count"])

Without session state, the counter would reset to 0 on every rerun.

Scope: per browser tab. Two tabs = two separate sessions. Closing the tab clears it. There is no cross-user or persistent storage — for that, use a database.


2. How do you read and write session state?

Two equivalent syntaxes:

# Dict-style
st.session_state["user_id"] = 42
uid = st.session_state["user_id"]

# Attribute-style (only for valid Python identifiers)
st.session_state.user_id = 42
uid = st.session_state.user_id

# Safe read with default
n = st.session_state.get("count", 0)

# Init pattern
st.session_state.setdefault("history", [])

Standard dict operations work: in, .get(), .pop(), iteration.


3. How do widgets interact with session state?

Every widget with a key parameter automatically writes its value to st.session_state[key]:

name = st.text_input("Name", key="user_name")
# After render, st.session_state["user_name"] holds the current value.
# `name` and `st.session_state["user_name"]` are the same value.

You can also seed a widget by setting state before the widget is created:

if "user_name" not in st.session_state:
    st.session_state["user_name"] = "Alice"
st.text_input("Name", key="user_name")

You cannot assign to st.session_state[key] after the widget with that same key has rendered in the same run — Streamlit raises an error. Either set it before the widget, or use a callback.


4. What are on_change callbacks?

Widgets accept an on_change callback that fires before the rerun, useful for side effects or for syncing state across widgets:

def on_select():
    st.session_state["selection_changed"] = True
    log.info(f"Selected: {st.session_state['kind']}")

st.selectbox("Kind", ["A", "B", "C"], key="kind", on_change=on_select)

Callbacks run in the order: on_change fires → script reruns. Pass arguments via args=(...) or kwargs={...}.

on_click is the same idea for buttons.


5. How do you reset state or “clear the form”?

# Reset specific keys
for key in ["name", "age", "email"]:
    if key in st.session_state:
        del st.session_state[key]
st.rerun()

# Reset everything
st.session_state.clear()
st.rerun()

Note: deleting a widget’s key resets that widget on the next render only if the widget is re-created. If you want a “Clear” button that resets a form, the standard pattern is:

if st.button("Clear"):
    st.session_state.pop("name", None)
    st.session_state.pop("age", None)
    st.rerun()

6. What is st.rerun() and when do you call it?

st.rerun() (formerly st.experimental_rerun) immediately stops the current run and starts a new one from the top.

Use cases:

  • Inside a callback that mutated state and wants the UI to reflect it on the next render.
  • After a form submit when the next render needs different layout.
  • After login: store the token, rerun, render the authenticated app.

Most of the time you don’t need it — Streamlit reruns automatically on widget changes. Reach for st.rerun() only when you’ve changed state programmatically (not via a widget) and want an immediate refresh.

if st.button("Login"):
    token = authenticate(...)
    st.session_state["token"] = token
    st.rerun()  # next run sees the token, shows the app

7. What’s the difference between widget value, widget key, and session_state?

value = st.text_input("Name", key="user_name", value="default")
  • Return value (value) — the current widget value, available immediately in this run.
  • key="user_name" — the slot in st.session_state where Streamlit writes/reads it. With a key, the widget is bound to that slot.
  • value="default" — the initial value used the first time, before the user touches it. Ignored if a value already exists in session state for that key.

After the widget renders, value == st.session_state["user_name"]. They’re the same data, accessed two ways.


8. How do you share state across pages in a multipage app?

st.session_state is shared across pages of the same Streamlit app — you don’t need any extra plumbing.

# page_1.py
st.session_state["user_id"] = login()

# page_2.py
uid = st.session_state.get("user_id")
if uid is None:
    st.warning("Please log in.")
    st.stop()

State persists as long as the browser tab stays open. Closing the tab or hitting “Rerun” cold (Ctrl-Shift-R) clears it.

For cross-session persistence (user closes tab, comes back), write to a DB or set cookies via a component like streamlit-cookies-controller.


9. Common session_state pitfalls

Pitfall Fix
Setting state for a widget after the widget renders → error Set state before the widget, or use on_change callback
Expecting state to persist after browser refresh It doesn’t. Use a DB or cookies
Two tabs of the same app share state They don’t. Each tab is its own session
Mutating a list/dict in state and not seeing the change The reference is the same; mutation works. If it doesn’t, you’re probably reassigning a local variable that shadows the state entry
Callback not firing on_change fires only when the widget value actually changes, not on every rerun
st.button value not surviving Buttons are momentary; persist the click into session state
Race conditions between callback writes and widget writes Callbacks run before the rerun completes — set non-widget state in callbacks; let widgets manage their own keys

Interview angle

  • “What is st.session_state?” - a per-session dict surviving reruns. It’s the only way to keep state, since local variables are recreated on every run.
  • “How long does it live?” - for the browser session. It’s not persistent storage and it’s not shared between users, so anything that must outlive a refresh belongs in a database.
  • “What’s the widget-key interaction?” - a widget with key="x" reads and writes st.session_state["x"], so you can set a widget’s value programmatically by assigning to it before the widget renders.