Speeding Up Streamlit Apps with st.cache_data: Practical Guide
Learn how to use Streamlit's st.cache_data to speed up expensive operations, avoid stale data, and tune cache size and TTL.
14 Sept 2026, 18:01 UTC

Problem: Slow reloads on every interaction
When a Streamlit script reruns on each widget change, any expensive function—like reading a large CSV or running a model—runs again from scratch. Users feel lag even for simple filters.
Thesis: Cache the result, not the computation
Streamlit’s st.cache_data decorator stores the return value of a function based on the hash of its arguments. On subsequent reruns with the same inputs, the cached value is returned instantly, eliminating the repeated work.
Worked example: Loading a CSV with a filter slider
import streamlit as st
import pandas as pd
import time
@st.cache_data
def load_data(path: str) -> pd.DataFrame:
start = time.time()
df = pd.read_csv(path)
st.write(f"Loaded {len(df)} rows in {time.time()-start:.2f}s")
return df
# App layout
DATA_PATH = "large_dataset.csv" # placeholder; replace with your file
df = load_data(DATA_PATH)
row_limit = st.slider("Show first N rows", 100, len(df), 500)
st.dataframe(df.head(row_limit))
Place the script in a file, e.g., app.py, and run:
streamlit run app.py
You need read access to DATA_PATH. The first execution prints the load time; moving the slider thereafter shows no additional log, indicating the cached DataFrame is reused.
Tuning the cache: TTL, size limits, and spinner
ttl(seconds) – automatically expires entries after the set time. Useful for data that updates periodically.max_entries– caps the number of distinct cached results; older entries are evicted.show_spinner– set toFalseto hide the default spinner while the function runs.
Example with a 10‑minute TTL and a maximum of three entries:
@st.cache_data(ttl=600, max_entries=3, show_spinner=False)
def load_data(path: str):
...
Limitations and how to verify correct behavior
Cache invalidation depends solely on the function’s arguments. If the function reads a file whose content changes but the path argument stays the same, Streamlit will not detect the change and will serve stale data.
To check:
- Run the app and note the load time printed by
st.write. - While the app is running, replace
large_dataset.csvwith a newer version (same name). - Adjust the slider again; if the printed load time does not reappear, the cache is still returning the old data.
- To force a refresh, change the argument—e.g., add a dummy parameter like a version string:
@st.cache_data
def load_data(path: str, version: str = "v1") -> pd.DataFrame:
...
Updating version to "v2" will create a new cache entry.
Memory usage: each cached DataFrame lives in the driver process. Monitor RAM with top or psutil; if growth becomes a concern, lower max_entries or set a shorter ttl.
Version note: st.cache_data is available from Streamlit 1.18. On older versions you will see a deprecation warning if you keep using the legacy @st.cache. Upgrade to stay supported.
Actionable closing
- Identify the expensive, pure function in your app (depends only on its inputs).
- Decorate it with
@st.cache_data. - Start with default settings; observe the first‑run latency versus subsequent interactions.
- If the data changes over time, add a
ttlor a version argument to bust the cache. - Watch memory usage; adjust
max_entriesorttlas needed.
With these steps you can turn frustrating lag into instant feedback while keeping the app’s behavior predictable.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.