# How to Implement Session State Management in Streamlit Apps: A Complete Guide

> Master Streamlit session state management with st.session_state. Learn to create persistent variables and store app data reliably across user interactions. Follow our complete guide.

- Repository: [Shubham Saboo/awesome-llm-apps](https://github.com/shubhamsaboo/awesome-llm-apps)
- Tags: how-to-guide
- Published: 2026-02-16

---

**Use `st.session_state` as a persistent dictionary to store variables across script reruns, initializing keys at the top of your app to prevent data loss on every user interaction.**

Streamlit's execution model reruns the entire script on every button click or input change, which resets standard Python variables. To build interactive LLM applications that remember chat history, API keys, or loaded documents, you must implement robust **session state management in Streamlit apps**. The `Shubhamsaboo/awesome-llm-apps` repository demonstrates production-grade patterns for persisting complex objects and avoiding redundant API calls.

## Why Streamlit Session State Management Is Essential

Streamlit apps operate on a stateless request-response cycle. Without `st.session_state`, every widget interaction wipes your data, forcing users to re-enter API keys or causing duplicate downloads of large transcripts. The session state dictionary survives reruns, acting as a global mutable store that lives outside the script's local scope.

## Initializing Session State Keys to Prevent KeyError

Always declare required keys at the top of your script using existence checks. This guarantees the key exists before you read or write it later, preventing `KeyError` exceptions on the first run.

```python
import streamlit as st

# Declare required keys once

keys = {
    "app": None,
    "current_video_url": None,
    "transcript_loaded": False,
    "transcript_text": None,
    "word_count": 0,
    "chat_history": [],
}
for k, default in keys.items():
    if k not in st.session_state:
        st.session_state[k] = default

```

## Conditional Resource Loading with Session State

Use session state flags to load heavy resources only when necessary. In [`advanced_llm_apps/chat_with_X_tutorials/chat_with_youtube_videos/chat_youtube.py`](https://github.com/Shubhamsaboo/awesome-llm-apps/blob/main/advanced_llm_apps/chat_with_X_tutorials/chat_with_youtube_videos/chat_youtube.py), the app fetches YouTube transcripts only when the URL changes or when the `transcript_loaded` flag is false.

```python
video_url = st.text_input("YouTube URL")
if video_url and (video_url != st.session_state.current_video_url
                  or not st.session_state.transcript_loaded):
    with st.spinner("Fetching transcript…"):
        title, transcript = fetch_video_data(video_url)
        if "No transcript" not in transcript:
            # Reset state for new video

            if st.session_state.transcript_loaded:
                st.session_state.app = None          # new vector store

                st.session_state.chat_history = []   # discard old chat

            # Store new data

            st.session_state.current_video_url = video_url
            st.session_state.transcript_text = transcript
            st.session_state.word_count = len(transcript.split())
            st.session_state.transcript_loaded = True

```

This pattern eliminates redundant API calls and ensures deterministic updates.

## Storing Complex Objects in Streamlit Session State

`st.session_state` can store any picklable object, including full library instances. The `awesome-llm-apps` repository stores an Embedchain `App` instance after creation to avoid rebuilding the vector database on every rerun.

```python
st.session_state.app = embedchain_bot(db_path, openai_access_token)

```

This approach is essential for maintaining expensive objects like database connections, ML models, or API clients across user interactions.

## Building Persistent Chat History with Session State

Append each user question and AI answer to a list stored in session state. This preserves the conversation even after the user sends a new prompt.

```python
prompt = st.text_input("Ask a question")
if prompt and st.session_state.transcript_loaded:
    answer = st.session_state.app.chat(prompt)
    st.session_state.chat_history.append((prompt, answer))
    st.write("**Answer:**", answer)

# Render history

if st.session_state.chat_history:
    with st.expander("Chat History"):
        for i, (q, a) in enumerate(st.session_state.chat_history, 1):
            st.markdown(f"**Q{i}:** {q}")
            st.markdown(f"**A{i}:** {a}")
            st.divider()

```

## Resetting and Clearing Session State

Provide users with a mechanism to clear data and start fresh. Explicitly wipe relevant entries and call `st.rerun()` to refresh the UI immediately.

```python
if st.button("🗑️ Clear Video"):
    for k in ["current_video_url", "transcript_loaded",
              "transcript_text", "word_count", "chat_history", "app"]:
        st.session_state[k] = keys[k]   # restore defaults

    st.rerun()                          # force UI refresh

```

This pattern ensures clean state transitions when switching between tasks or users.

## Real-World Examples from awesome-llm-apps

The `Shubhamsaboo/awesome-llm-apps` repository provides several reference implementations demonstrating these patterns:

| File | Role | Implementation Details |
|------|------|------------------------|
| [`advanced_llm_apps/chat_with_X_tutorials/chat_with_youtube_videos/chat_youtube.py`](https://github.com/Shubhamsaboo/awesome-llm-apps/blob/main/advanced_llm_apps/chat_with_X_tutorials/chat_with_youtube_videos/chat_youtube.py) | Full session-state workflow for transcript loading, vector store handling, chat history, and reset logic. | [View Source](https://github.com/Shubhamsaboo/awesome-llm-apps/blob/main/advanced_llm_apps/chat_with_X_tutorials/chat_with_youtube_videos/chat_youtube.py) |
| [`advanced_llm_apps/chat_with_X_tutorials/chat_with_youtube_videos/test_session_state.py`](https://github.com/Shubhamsaboo/awesome-llm-apps/blob/main/advanced_llm_apps/chat_with_X_tutorials/chat_with_youtube_videos/test_session_state.py) | Unit-test that mimics session-state flow without a Streamlit UI, useful for verifying logic. | [View Source](https://github.com/Shubhamsaboo/awesome-llm-apps/blob/main/advanced_llm_apps/chat_with_X_tutorials/chat_with_youtube_videos/test_session_state.py) |
| [`voice_ai_agents/voice_rag_openaisdk/rag_voice.py`](https://github.com/Shubhamsaboo/awesome-llm-apps/blob/main/voice_ai_agents/voice_rag_openaisdk/rag_voice.py) | Session-state for storing API keys, selected voice, processed documents, and multi-step pipeline state. | [View Source](https://github.com/Shubhamsaboo/awesome-llm-apps/blob/main/voice_ai_agents/voice_rag_openaisdk/rag_voice.py) |
| [`starter_ai_agents/opeani_research_agent/research_agent.py`](https://github.com/Shubhamsaboo/awesome-llm-apps/blob/main/starter_ai_agents/opeani_research_agent/research_agent.py) | Session-state managing research workflow state including conversation ID and collected facts. | [View Source](https://github.com/Shubhamsaboo/awesome-llm-apps/blob/main/starter_ai_agents/opeani_research_agent/research_agent.py) |

## Summary

- **Initialize early**: Check `if 'key' not in st.session_state` at the top of your script to prevent `KeyError` exceptions.
- **Load conditionally**: Compare current inputs against stored session values to avoid redundant API calls and expensive recomputation.
- **Store rich objects**: Use `st.session_state` to persist picklable instances like database connections, ML models, and API clients.
- **Persist conversations**: Append user queries and responses to a list in session state to maintain chat history across reruns.
- **Reset cleanly**: Provide explicit clear buttons that restore default values and call `st.rerun()` to refresh the UI.

## Frequently Asked Questions

### How do I prevent Streamlit from resetting my variables on every interaction?

Store variables in `st.session_state`, which acts as a persistent dictionary that survives script reruns. Initialize keys at the top of your script using `if 'key' not in st.session_state` to ensure they exist before use.

### Can I store complex objects like machine learning models in session state?

Yes, `st.session_state` can store any picklable Python object, including ML models, database connections, and third-party library instances like Embedchain `App` objects. This avoids expensive reinitialization on every user interaction.

### What is the best way to clear or reset session state in Streamlit?

Iterate over the keys you want to reset and restore them to their default values, then call `st.rerun()` to immediately refresh the UI. This pattern is useful when switching between videos, resetting chats, or clearing user data.

### How do I avoid making duplicate API calls when the user interacts with my app?

Store the last used input value (like a URL) in `st.session_state` and compare it against the current input before executing the API call. Only fetch new data when the input changes or when a loading flag indicates the resource is not yet cached.