Dashboards (Streamlit / Dash / Gradio)
Turn notebooks into shareable apps: Streamlit for rapid data dashboards, Dash for Plotly-centric products, Gradio for model demos.
Recipe
Quick-reference recipe card - copy-paste ready.
# streamlit_app.py
import streamlit as st
import pandas as pd
@st.cache_data
def load():
return pd.read_parquet("sales.parquet")
df = load()
region = st.selectbox("Region", sorted(df["region"].unique()))
filtered = df[df["region"] == region]
st.line_chart(filtered.set_index("ordered_at")["revenue"])When to reach for this:
- Stakeholders need filters without Jupyter
- ML team wants a quick scoring UI (Gradio)
- Plotly dashboards with callbacks and auth (Dash)
- Internal tools shipping in days, not months
Working Example
# Minimal Streamlit + Plotly pattern
import streamlit as st
import pandas as pd
import plotly.express as px
st.set_page_config(page_title="Revenue Explorer", layout="wide")
@st.cache_data(show_spinner="Loading sales...")
def load_sales() -> pd.DataFrame:
# Replace with read_parquet in production
return pd.DataFrame(
{
"ordered_at": pd.date_range("2025-01-01", periods=30, freq="D", tz="UTC"),
"region": ["East", "West"] * 15,
"revenue": range(30),
}
)
df = load_sales()
col1, col2 = st.columns(2)
with col1:
regions = st.multiselect("Regions", sorted(df["region"].unique()), default=["East"])
with col2:
min_rev = st.slider("Min revenue", 0, int(df["revenue"].max()), 0)
filtered = df[df["region"].isin(regions) & (df["revenue"] >= min_rev)]
fig = px.line(filtered, x="ordered_at", y="revenue", color="region")
st.plotly_chart(fig, use_container_width=True)
st.dataframe(filtered, use_container_width=True)What this demonstrates:
cache_dataavoids reloading Parquet on every widget change- Layout with columns and wide page config
- Plotly chart embedded with responsive width
- Filters composed with pandas boolean masks
Deep Dive
How It Works
- Streamlit reruns the script top-to-bottom on interaction; widgets are declarative.
- Dash uses Flask + React callbacks - better for complex state and URLs.
- Gradio wraps a function as IO blocks - ideal for
predict(image) -> label. - All three run behind
uvicorn/built-in server - put reverse proxy auth in production.
Framework Picker
| Framework | Sweet spot |
|---|---|
| Streamlit | pandas/plots + filters in hours |
| Dash | Multi-page Plotly apps, enterprise |
| Gradio | Hugging Face model demos |
Python Notes
# Dash skeleton (separate app.py)
from dash import Dash, html, dcc, Input, Output
import plotly.express as px
app = Dash(__name__)
app.layout = html.Div([dcc.Graph(id="chart"), dcc.Dropdown(id="region")])
# @app.callback(Output("chart", "figure"), Input("region", "value")) ...Gotchas
- No cache on heavy load - every slider tick reloads Parquet. Fix:
@st.cache_data/ Dashdcc.Store. - Embedding secrets in repo - API keys in
st.secretsor env vars only. Fix: never commit credentials. - Unbounded multiselect - selecting all regions replots huge frames. Fix: cap selection or aggregate server-side.
- Dash callback cycles - mutual Outputs without guards loop. Fix: single source of truth state store.
- Gradio for BI dashboards - wrong tool - layout and SQL filters are awkward. Fix: Streamlit/Dash instead.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Panel/HoloViz | PyData stack integration | Team wants simplest path (Streamlit) |
| FastAPI + React | Custom UX and auth | Need app in one afternoon |
| Superset/Metabase | Warehouse BI on SQL | Heavy Python transforms in UI |
| Observable / Hex | Hosted notebook products | Self-hosted Python requirement |
FAQs
Streamlit or Dash first?
- Streamlit for single-team analytics prototypes.
- Dash when you need URL routing, callbacks, and IT-owned deployment patterns.
How do I deploy Streamlit?
- Docker +
streamlit run app.py --server.port=8501. - Put OAuth at reverse proxy (nginx, Cloudflare Access).
How do I share Gradio?
import gradio as gr
demo = gr.Interface(fn=predict, inputs="image", outputs="label")
demo.launch()- Use
share=Trueonly for demos, not production data.
Can I use Polars in Streamlit?
- Yes - convert to pandas for
st.line_chartor use Plotly/Altair directly.
How do I test dashboards?
- Streamlit AppTest (built-in) for widget flows.
- Dash: pytest with
dash.testingfixtures.
How do I handle auth?
- Streamlit-Authenticator or proxy SSO.
- Do not roll crypto yourself.
Why is my app slow?
- Profile data load and cache it.
- Aggregate before plotting - do not ship million-row dataframes to browser.
Can Dash use pandas 2.2+?
- Yes - Dash is agnostic to DataFrame library.
How do I version app + data together?
- Pin data partition in app config (
DATA_DT=2025-01-15). - Log git SHA in footer.
Gradio vs Streamlit for LLM demos?
- Gradio defaults for chat/image IO.
- Streamlit when charts and SQL filters dominate.
Related
- Plotly - charts in Dash and Streamlit
- Plotting Large Data - keep apps responsive
- Visualization Best Practices - honest KPIs in apps
- Data Analysis Basics - data prep upstream
Stack versions: This page was written for Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.