# Streamlit & Gradio > Streamlit and Gradio are the two dominant frameworks for building rapid data apps and ML demos in 2026 : no frontend skills required. Streamlit is for data dashboards; Gradio is for ML model interfaces. --- ## 1. Comparison: Streamlit vs Gradio | Feature | Streamlit | Gradio | | :--- | :--- | :--- | | **Best for** | Data apps, dashboards, exploratory tools | ML model demos, inputβ†’output interfaces | | **UI paradigm** | Script-based, top-to-bottom | Component grid (Interface or Blocks) | | **State** | `st.session_state` | Implicit per-component | | **Layout** | Columns, tabs, sidebar, expander | Rows, columns, tabs (Blocks) | | **HuggingFace Spaces** | Supported βœ… | Native SDK βœ… | | **Deployment** | Streamlit Cloud (free) | HF Spaces (free) | | **Interactivity** | Widget-driven script reruns | Event-driven callbacks | | **Multi-page** | Native (pages/ folder) | via Blocks + Tab | --- ## 2. Streamlit : Core Concepts ### How Streamlit Works ``` Every user interaction β†’ full script re-runs top to bottom. State is preserved via st.session_state. Caching prevents re-running expensive functions. ``` ### Minimal App ```python import streamlit as st import pandas as pd import matplotlib.pyplot as plt st.set_page_config(page_title="My App", page_icon="πŸ“Š", layout="wide") st.title("Sales Dashboard") st.subheader("2024 Revenue Analysis") st.write("Welcome! Explore the data below.") # markdown supported df = pd.read_csv("sales.csv") st.dataframe(df) # interactive table st.table(df.head()) # static table # Run: streamlit run app.py ``` --- ## 3. Streamlit Widgets ```python import streamlit as st # Text input name = st.text_input("Enter your name", value="Alice") text = st.text_area("Description", height=200) number = st.number_input("Age", min_value=0, max_value=120, value=25) # Selection option = st.selectbox("Country", ["India", "USA", "UK"]) options = st.multiselect("Tags", ["AI", "Data", "Python"], default=["AI"]) choice = st.radio("Plan", ["Free", "Pro", "Enterprise"]) # Boolean agree = st.checkbox("I agree to terms") dark_mode = st.toggle("Dark Mode") # Streamlit 1.26+ # Sliders age = st.slider("Age", min_value=18, max_value=65, value=30) range_val = st.slider("Salary Range", 0, 200000, (40000, 120000)) # tuple = range # Dates from datetime import date selected_date = st.date_input("Pick a date", value=date.today()) # File upload uploaded = st.file_uploader("Upload CSV", type=["csv", "xlsx"]) if uploaded: df = pd.read_csv(uploaded) st.dataframe(df) # Button if st.button("Run Analysis"): st.success("Analysis complete!") # Download button csv_data = df.to_csv(index=False).encode() st.download_button("Download CSV", csv_data, "data.csv", "text/csv") ``` --- ## 4. Layout & Columns ```python import streamlit as st # Sidebar with st.sidebar: st.header("Filters") country = st.selectbox("Country", ["All", "India", "USA"]) year = st.slider("Year", 2020, 2024, 2023) # Columns (equal width) col1, col2, col3 = st.columns(3) with col1: st.metric("Revenue", "β‚Ή4.2M", delta="+12%") # βœ… delta shows green/red with col2: st.metric("Users", "12,400", delta="-3%") with col3: st.metric("Conversion", "3.8%", delta="+0.5%") # Columns (custom ratio) left, right = st.columns([2, 1]) # 2:1 ratio with left: st.line_chart(df) with right: st.write("Summary stats") # Tabs tab1, tab2, tab3 = st.tabs(["Overview", "Details", "Raw Data"]) with tab1: st.bar_chart(df["revenue"]) with tab2: st.dataframe(df.describe()) with tab3: st.dataframe(df) # Expander (collapsible) with st.expander("Show raw data"): st.dataframe(df) # Container with st.container(): st.write("Grouped content") ``` --- ## 5. Charts & Visualizations ```python import streamlit as st import pandas as pd import matplotlib.pyplot as plt import plotly.express as px # Built-in charts (simple, fast) st.line_chart(df[["revenue", "cost"]]) # time series st.bar_chart(df.set_index("month")["sales"]) # bar st.area_chart(df[["a", "b"]]) st.scatter_chart(df, x="age", y="salary", color="department") # Matplotlib fig, ax = plt.subplots() ax.hist(df["salary"], bins=30, color="steelblue") ax.set_xlabel("Salary") st.pyplot(fig) # Plotly (interactive : best choice βœ…) fig = px.scatter(df, x="age", y="salary", color="department", title="Salary vs Age", hover_data=["name"]) st.plotly_chart(fig, use_container_width=True) fig2 = px.choropleth(df, locations="country", color="revenue", locationmode="country names") st.plotly_chart(fig2) ``` --- ## 6. Caching : Critical for Performance ```python import streamlit as st import pandas as pd # @st.cache_data : for data: DataFrames, serializable objects βœ… @st.cache_data def load_data(filepath: str) -> pd.DataFrame: return pd.read_csv(filepath) # runs once, cached for all users βœ… # @st.cache_resource : for non-serializable: ML models, DB connections βœ… @st.cache_resource def load_model(): import joblib return joblib.load("model.pkl") # loaded once, shared across sessions βœ… df = load_data("data.csv") model = load_model() # Cache with TTL (time-to-live) @st.cache_data(ttl=3600) # refresh every hour def fetch_live_data(): return pd.read_sql("SELECT * FROM sales", con) # Clear cache if st.button("Refresh Data"): load_data.clear() # clear specific function cache st.cache_data.clear() # clear all cache ``` ### Cache Decision Table | Resource | Use | Why | | :--- | :--- | :--- | | DataFrames, lists, dicts | `@st.cache_data` | Serializable, safe to copy | | ML models (sklearn, torch) | `@st.cache_resource` | Not serializable, share across sessions | | DB connections | `@st.cache_resource` | Connection pooling, share safely | | API responses | `@st.cache_data(ttl=300)` | Refresh periodically | --- ## 7. Session State ```python import streamlit as st # Initialize state if "count" not in st.session_state: st.session_state.count = 0 if "history" not in st.session_state: st.session_state.history = [] # Modify state if st.button("Increment"): st.session_state.count += 1 st.write(f"Count: {st.session_state.count}") # Chat interface pattern for msg in st.session_state.history: with st.chat_message(msg["role"]): # "user" or "assistant" st.write(msg["content"]) if prompt := st.chat_input("Ask something..."): st.session_state.history.append({"role": "user", "content": prompt}) response = "Echo: " + prompt st.session_state.history.append({"role": "assistant", "content": response}) st.rerun() # trigger rerun to show new messages ``` --- ## 8. Multi-Page Apps ``` project/ β”œβ”€β”€ app.py ← main entry point (st.Page or pages/ folder) └── pages/ β”œβ”€β”€ 1_Overview.py β”œβ”€β”€ 2_Analysis.py └── 3_Settings.py Streamlit auto-discovers pages/ folder. Page names are derived from filenames (underscores β†’ spaces, numbers = order). ``` ```python # Modern API (Streamlit 1.36+) import streamlit as st pg = st.navigation([ st.Page("pages/overview.py", title="Overview", icon="🏠"), st.Page("pages/analysis.py", title="Analysis", icon="πŸ“Š"), ]) pg.run() ``` --- ## 9. Deployment ### Streamlit Community Cloud (Free) ``` 1. Push app to GitHub (public or private repo) 2. Go to share.streamlit.io β†’ "New app" 3. Select repo, branch, and app.py path 4. Add secrets in the Secrets panel (replaces .env) Requirements: - requirements.txt (or pyproject.toml) - app.py as entrypoint Secrets (st.secrets): [database] host = "db.example.com" password = "secret" Access in code: st.secrets["database"]["host"] st.secrets["API_KEY"] ``` --- ## 10. Gradio : Core Concepts Gradio wraps Python functions into web UIs. Two APIs: `Interface` (simple) and `Blocks` (full control). ### `gr.Interface` : Simplest Approach ```python import gradio as gr def predict(text: str, temperature: float) -> str: return f"Echo: {text} (temp={temperature})" demo = gr.Interface( fn=predict, inputs=[ gr.Textbox(label="Input Text", placeholder="Type here..."), gr.Slider(0.0, 1.0, value=0.7, label="Temperature"), ], outputs=gr.Textbox(label="Output"), title="My Model Demo", description="Enter text and click Submit", examples=[["Hello world", 0.5], ["Test", 0.9]], # βœ… pre-filled examples flagging_mode="never", # disable flagging button ) demo.launch(share=True) # share=True creates a public link βœ… # demo.launch(server_port=7860, server_name="0.0.0.0") # for Docker ``` ### Common Gradio Components ```python import gradio as gr # Text gr.Textbox(label="Text", lines=3, placeholder="Type...") gr.Markdown("**Bold** and *italic* text") # Numbers gr.Slider(minimum=0, maximum=100, step=1, value=50, label="Slider") gr.Number(label="Age", value=25, minimum=0) # Selection gr.Dropdown(choices=["BERT", "GPT-4", "Mistral"], label="Model") gr.Radio(choices=["Low", "Medium", "High"], label="Quality") gr.Checkbox(label="Use GPU") gr.CheckboxGroup(["English", "French", "Hindi"], label="Languages") # Files & Media gr.File(label="Upload file") gr.Image(type="numpy") # returns numpy array gr.Image(type="pil") # returns PIL Image gr.Audio(type="filepath") # returns audio file path gr.Video(label="Upload video") # Output only gr.Label(label="Prediction") # classification label + probabilities gr.HighlightedText(label="NER") # colored text for token labels gr.Plot(label="Chart") # matplotlib figure gr.DataFrame(label="Table") # pandas DataFrame display gr.JSON(label="Raw output") # pretty JSON gr.HTML(label="HTML output") ``` --- ## 11. `gr.Blocks` : Full Layout Control ```python import gradio as gr import numpy as np def segment_image(image, threshold): return (image > threshold * 255).astype(np.uint8) * 255 def classify(image): return {"cat": 0.85, "dog": 0.12, "other": 0.03} with gr.Blocks(theme=gr.themes.Soft(), title="Image Lab") as demo: gr.Markdown("# Image Analysis Lab") with gr.Tab("Segmentation"): with gr.Row(): with gr.Column(): img_in = gr.Image(label="Input Image") threshold = gr.Slider(0.0, 1.0, 0.5, label="Threshold") btn = gr.Button("Segment", variant="primary") with gr.Column(): img_out = gr.Image(label="Segmented") btn.click(fn=segment_image, inputs=[img_in, threshold], outputs=img_out) with gr.Tab("Classification"): img_clf = gr.Image(label="Upload Image") label_out = gr.Label(num_top_classes=3) img_clf.change(fn=classify, inputs=img_clf, outputs=label_out) # auto on change βœ… demo.launch() ``` ### Event Handling ```python # Trigger on button click btn.click(fn=my_function, inputs=[text_in], outputs=[text_out]) # Trigger on component change (no button) slider.change(fn=update_preview, inputs=slider, outputs=preview) text_in.submit(fn=process, inputs=text_in, outputs=text_out) # on Enter key # Loading state btn.click(fn=slow_fn, inputs=[], outputs=out, show_progress=True) # Streaming output (for LLMs) def stream_text(prompt): for char in "Hello world! This streams character by character.": yield char text_in.submit(fn=stream_text, inputs=text_in, outputs=text_out) ``` --- ## 12. Gradio with HuggingFace Models ```python import gradio as gr from transformers import pipeline # Sentiment analysis interface classifier = pipeline("sentiment-analysis") def analyze(text): result = classifier(text)[0] return {result['label']: result['score']} gr.Interface( fn=analyze, inputs=gr.Textbox(label="Text"), outputs=gr.Label(label="Sentiment"), ).launch() # Image captioning captioner = pipeline("image-to-text", model="Salesforce/blip-image-captioning-base") def caption(image): return captioner(image)[0]["generated_text"] gr.Interface( fn=caption, inputs=gr.Image(type="pil"), outputs=gr.Textbox(label="Caption"), ).launch() ``` --- ## 13. Deploying to HuggingFace Spaces ``` Minimum files for a Gradio Space: app.py ← must call demo.launch() at bottom requirements.txt ← dependencies (gradio, transformers, etc.) For Streamlit Space: app.py ← streamlit run app.py entrypoint requirements.txt Push via git: git clone https://huggingface.co/spaces/your-username/space-name # add files git add . && git commit -m "add app" git push Environment variables: Set in Space Settings β†’ Variables and Secrets Access in code: import os; os.environ["MY_KEY"] ``` --- ## 14. Quick Reference ``` Streamlit commands: st.write() β†’ auto-renders: str, df, dict, chart, etc. st.dataframe(df) β†’ interactive sortable table st.plotly_chart(fig) β†’ Plotly figure st.pyplot(fig) β†’ matplotlib figure st.metric(label, val, delta) β†’ KPI tile with delta indicator st.success/error/warning/info("msg") β†’ colored alert boxes st.spinner("Loading...") β†’ with st.spinner(): do_work() st.progress(0.5) β†’ 0.0–1.0 progress bar st.balloons() β†’ celebration animation 🎈 st.rerun() β†’ force rerun ``` | Task | Streamlit | Gradio | | :--- | :--- | :--- | | Data dashboard | `st.plotly_chart` + `st.dataframe` | Not ideal | | Model demo (image in, label out) | Verbose | `gr.Interface(fn, inputs, outputs)` | | Chat interface | `st.chat_message` + `st.chat_input` | `gr.ChatInterface(fn)` βœ… | | File upload β†’ processing | `st.file_uploader` | `gr.File` or `gr.Image` | | Deploy free | Streamlit Community Cloud | HuggingFace Spaces | | Share demo link | `st.run --server...` | `demo.launch(share=True)` βœ… | | LLM streaming output | Custom with `st.write_stream` | `yield` in function βœ… |