What Makes Streamlit Different
Streamlit converts a Python script into a web app by running it top-to-bottom every time a user interacts with a widget. No HTML, no JavaScript, no React. Just Python.
This execution model is the key to understanding Streamlit. Every button click, slider move, or text input triggers a full script rerun. This sounds inefficient, but Streamlit optimizes it with caching.
Your First Streamlit App
import streamlit as st
import pandas as pd
st.title("Sales Dashboard")
uploaded = st.file_uploader("Upload CSV", type="csv")
if uploaded:
df = pd.read_csv(uploaded)
region = st.selectbox("Filter by region", df["region"].unique())
filtered = df[df["region"] == region]
st.dataframe(filtered)
st.bar_chart(filtered.groupby("product")["revenue"].sum())
Run with: streamlit run app.py
Caching: The Essential Optimization
import streamlit as st
import pandas as pd
from sentence_transformers import SentenceTransformer
@st.cache_data # cache data loading — reruns only when inputs change
def load_data(path: str) -> pd.DataFrame:
return pd.read_parquet(path)
@st.cache_resource # cache heavy objects — loaded once, shared across sessions
def load_model() -> SentenceTransformer:
return SentenceTransformer("all-MiniLM-L6-v2")
df = load_data("embeddings.parquet") # cached after first run
model = load_model() # loaded once, reused
Use @st.cache_data for data (serializable, per-user). Use @st.cache_resource for models and database connections (shared across all users).
Building an LLM Chatbot with st.chat_message
import streamlit as st
from openai import OpenAI
client = OpenAI(api_key=st.secrets["OPENAI_API_KEY"])
if "messages" not in st.session_state:
st.session_state.messages = []
for msg in st.session_state.messages:
with st.chat_message(msg["role"]):
st.write(msg["content"])
if prompt := st.chat_input("Ask anything..."):
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.write(prompt)
with st.chat_message("assistant"):
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=st.session_state.messages,
stream=True,
)
content = st.write_stream(response)
st.session_state.messages.append({"role": "assistant", "content": content})
Session State for Persistence
import streamlit as st
if "counter" not in st.session_state:
st.session_state.counter = 0
if st.button("Increment"):
st.session_state.counter += 1
st.write(f"Count: {st.session_state.counter}")
Session state persists across reruns for a single user session.
Secrets Management
# .streamlit/secrets.toml (never commit this)
OPENAI_API_KEY = "sk-..."
DATABASE_URL = "mongodb+srv://..."
import streamlit as st
key = st.secrets["OPENAI_API_KEY"]
On Streamlit Community Cloud, secrets are set in the app dashboard.
Deploying for Free
- Push your app to GitHub
- Go to share.streamlit.io
- Connect your repo and set the main file path
- Add secrets in the dashboard
- Deploy — public URL in seconds
Resources: Streamlit, GitHub, app gallery.