Tech With Galvan
Courses/Streamlit: 20 Real Apps/Streamlit Fundamentals

Session State — Giving Your App Memory

30 min • Beginner • Module 2

Solve the #1 Streamlit confusion: why variables vanish between clicks, and how session state fixes it.

Session state — giving your app memory

Here’s the mistake every Streamlit beginner makes:

import streamlit as st

count = 0

if st.button("Add one"):
    count = count + 1

st.write(f"Count: {count}")

Click the button. The count shows 1. Click again… still 1. Forever 1. Why?

Why variables vanish

Remember the rerun model: every click re-runs the whole script from the top. So count = 0 runs again, wiping your increment before it ever displays. Plain variables have the memory of a goldfish.

The fix: st.session_state

st.session_state is a dictionary that survives reruns. It’s your app’s memory:

import streamlit as st

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

if st.button("Add one"):
    st.session_state.count += 1

st.write(f"Count: {st.session_state.count}")

Click away now — it climbs: 1, 2, 3… The value lives in session state, so reruns no longer erase it.

The pattern to memorize

if "my_key" not in st.session_state:
    st.session_state.my_key = initial_value

Initialize before you read. This one pattern prevents the most common Streamlit crash (KeyError) — check the key exists first, set a default if not.

Where you’ll use it (real examples from this course)

  • Quiz app — which question number the user is on
  • Countdown timer — the deadline they’re counting toward
  • To-do list — the list of tasks between actions
  • Typing test — when the timer started

If an app needs to remember anything longer than one rerun, it goes in session state.

Clearing memory (restarts)

if st.button("Restart"):
    for key in ["count", "history"]:
        st.session_state.pop(key, None)
    st.rerun()

pop(key, None) removes a key safely (no crash if it’s missing) — that’s how apps “start over”.

Common Errors & Fixes

  • KeyError: ‘count’ — you read st.session_state.count before initializing it. Use the initialize-first pattern.
  • Value updates but display lags one click behind — you’re displaying before updating. Update the state first, then display (or call st.rerun() after changing state).
  • State resets when I restart the app — session state lives per browser session; closing the tab or restarting the server clears it. That’s expected.

What to try next

Build a click-counter with a Reset button and a “highest count ever” that survives resets (hint: store two keys, only reset one).

Checkpoint: You can build a counter that survives reruns and a list that grows with each button press.

Key takeaways

  • • Why plain variables reset on every interaction
  • • st.session_state as your app's memory
  • • The initialize-before-read pattern