Without caching, every click reloads data and recomputes everything, making apps slow and expensive.
Because scripts rerun constantly, caching is essential. Streamlit offers two decorators.
`@st.cache_data` caches the return value of a function keyed by its arguments (and its source code). Use it for anything that produces data: reading files, querying a database, calling an API, transforming DataFrames.
- Each call returns a copy of the cached value, so callers can mutate the result safely.
- The value must be pickleable.
ttl="1h"expires entries, essential for data that changes;max_entries=bounds memory.- Arguments must be hashable by Streamlit; prefix a parameter with an underscore (
_conn) to exclude it from the key.
`@st.cache_resource` caches one shared object, without copying, for things that should exist once per server: database connections, ML models, API clients.
- All sessions get the same object, so it must be thread-safe, and mutating it affects every user.
Both caches are global: shared across all users and sessions of the app process, which makes them efficient but means never caching user-specific secrets under a key other users can hit.
Invalidate with my_func.clear() for one function, or st.cache_data.clear() for all. Editing the function's code also invalidates its cache.
Pick by the question "is this data or a connection/model?": DataFrames and results go to cache_data; live objects go to cache_resource.
Example
import time
import pandas as pd
import streamlit as st
@st.cache_data(ttl="10m", show_spinner="Loading sales...")
def load_sales(region: str) -> pd.DataFrame:
time.sleep(2) # pretend this is a slow query
return pd.DataFrame({"region": [region] * 3, "amount": [10, 20, 30]})
@st.cache_resource
def get_model():
time.sleep(2) # e.g. load a large ML model
return {"name": "churn-v3"} # one object shared by all users
region = st.selectbox("Region", ["EU", "US"])
df = load_sales(region) # slow the first time per region, instant after
df["amount"] *= 2 # safe: cache_data returned a copy
st.dataframe(df)
st.write("Model:", get_model()["name"])
if st.button("Refresh data"):
load_sales.clear() # drop cached entries for this functionRemember
cache_datacaches return values per argument set and returns copiescache_resourcecaches one shared, uncopied object such as a connection or model- Use
ttlfor changing data and.clear()to invalidate
Watch out for
- Using cache_resource for a DataFrame that callers then mutate, changing it for every user.