Skip to content
MP

Miljana Pinić

1 article

June 8, 2026

Streamlit Behind the Scenes: Session and Cache Explained

Software Development

Streamlit Behind the Scenes: Session and Cache Explained

What is Streamlit? Working with tons of data can easily get overwhelming. Have you ever wanted to explore and visualize it quickly, without all the frontend setup? Streamlit has you covered. Streamlit is an open-source framework that enables engineers to create dynamic data web applications using Python without any frontend setup. There is no need to use HTML, CSS, JavaScript, or any complex web framework. Python knowledge is everything you need. To get started with Streamlit, simply follow the official installation guide. The Rerun Model - How Streamlit Actually Executes Coding using Streamlit is easy once you understand how it actually works. Streamlit applications are Python scripts that run from top to bottom and have a specific flow and architecture. Like any Python script, Streamlit apps execute statements sequentially from top to bottom. Every user interaction with the component triggers the entire script to be rerun. The need for this stems from Streamlit's simplicity: write apps the same way plain Python scripts are written. When coding with Streamlit, you don’t need to think in terms of callbacks and events, because each user interaction triggers a full rerun of the script to update the UI. Sessions - Preserving State Across Reruns A session is a single instance of the state of a running Streamlit application. Application state consists of the state of every UI component and is stored in-memory on the server as st.session_state. Every user session has its own st.session_state. For example, if we open our application in two tabs, each tab will have its own session. Since the script is rerun on each interaction, it is necessary to save the components' previous state to render them accurately on the next run. Streamlit components have an optional parameter key that, when set, points to the session_state key that holds the component's state, as shown in the example below. import streamlit as stst.radio(    label="Choose a movie genre",    options=[        "Comedy",        "Drama",        "Romance",    ],   key="radio_movie_genre")if st.session_state.radio_movie_genre == "Comedy":st.text("You have chosen Comedy")elif st.session_state.radio_movie_genre == "Drama":  st.text("You have chosen Drama")elif st.session_state.radio_movie_genre == "Romance":st.text("You have chosen Romance") The important thing to remember is that values in session state are only available in the current active session.  Caching - Avoiding Redundant Work The fact that the app is rerun frequently can cause significant time and memory overhead when loading data from the database or fetching it via an API call. On every rerun, the data would be fetched again. To prevent this, Streamlit provides a built-in caching mechanism. Cached values can be accessed by all users/tabs of the app, unlike a session, which can be accessed only by a single user/tab.  @st.cache_data Since the cached values are used by multiple sessions, modifying the data can cause a race condition. To ensure the original data state remains unchanged, Streamlit returns a copy of the cached data instead of the original. To cache the return value of the function that fetches the data, append the decorator @st.cache_data above its declaration. @st.cache_datadef fetch_the_data(param1:str, param2:str) -> List: # API call or DB access return data To determine whether to call the function or read the cached value, Streamlit checks the two conditions:  If the number, variable types, and values of the input parameters are the same  If the code in the function is the same But how does Streamlit actually check whether the input parameter values are the same? This is where hashing comes into play. The input values are hashed and stored so that, on subsequent calls, they can be compared with new values. On the first run, the function is called; on subsequent runs, if the conditions are met, the returned value is read from the cache, and a copy is created. By setting the ttl (time-to-live) of the cache decorator, the time during which the cache exists is determined. After that time, the cached value is no longer available, and the function will be executed again on a call. #The cached data will be stored for 3600s ( 1 hour )@st.cache_data(ttl=3600) def fetch_the_data(param1:str, param2:str) -> List: # API call or DB access return data @st.cache_resource For caching global resources such as ML models or database connections, which are typically expensive to create, Streamlit uses the @st.cache_resource decorator. Unlike @st.cache_data, the @st.cache_resource decorator does not create and return a copy of the cached value; instead, it returns the same shared instance (singleton) of the resource every time. That makes it a perfect fit for caching resources that are not meant to be duplicated. This helps avoid unnecessary memory usage, especially for large objects like ML models or database connections. However, because a cached resource is shared across all users, sessions, and reruns, it raises thread-safety concerns. This means that multiple users or threads can access the resource simultaneously and read or write to it concurrently. Because of this, it is important to ensure that cached resources are thread-safe. A thread-safe resource behaves correctly when multiple threads access and use it simultaneously, without producing inconsistent results.  In general, cached resources should be treated as read-only or designed to handle concurrent access. The example of usage is shown below: import streamlit as stimport psycopg2import pandas as [email protected]_resourcedef get_db_connection(): conn = psycopg2.connect( host="host", database="database", user="user", password="password" ) return conn# Function that fetches the data from dbdef fetch_data(query): conn = get_db_connection() # uses cached connection df = pd.read_sql_query(query, conn) return df The illustration below, from the official Streamlit documentation, represents, in a simple way, what can be cached using these two decorators from Streamlit. Summary / Key Takeaways Creating a modern, interactive UI to visualize and manage large datasets is both fun and easy with Streamlit. The diagram below shows how Streamlit works under the hood. From user interaction that triggers the script rerun, through session state restoration, to the execution of the script, with caching as a secret weapon for improving performance and reducing memory costs. Now that you understand how Streamlit handles reruns, state, and caching, you are all set to build more powerful Streamlit apps. These concepts are what make Streamlit seem simple, yet powerful and efficient in the background. References: Streamlit documentation.

Ready to Achieve More?

We’ll help you reach your goals quickly with an easy and straightforward process to kick off our collaboration. Here’s what happens next.

STEP 1

Discovery Call

Let’s chat to understand your company, project needs, and answer any questions along the way.

STEP 2

Free Consultation

Work closely with our experts to explore the right solutions for your business.

STEP 3

Collaboration Proposal

We'll recommend the best strategy for your goals, ensuring you get the most from our expertise.

STEP 4

30-Day Cancellation
Policy Contract

Spoiler: It’s Never Been Used

Enjoy peace of mind while we deliver excellence from day one—our track record speaks for itself.

Services you're interested in (Optional)