Gradio¶
Turn any Python function into a shareable UI. Gradio is built for ML demos: wrap a model in a few lines, get uploads, live inference, and a public link — no web code required.
Table of Contents¶
- Installation / Setup
- Core Syntax
- Common Operations
- Practical Examples
- Common Patterns
- Best Practices
- Common Mistakes
- Troubleshooting
- Related Cheat Sheets
- Official Documentation
Installation / Setup¶
Two APIs:
| API | Use for |
|---|---|
gr.Interface(fn, inputs, outputs) |
One function → one UI (the 80% case) |
gr.Blocks |
Custom layouts, multiple components, complex event wiring |
Core Syntax¶
Minimal interface¶
import gradio as gr
def greet(name):
return f"Hello {name}!"
demo = gr.Interface(fn=greet, inputs="text", outputs="text")
demo.launch()
Components¶
| Component | Typical usage |
|---|---|
gr.Textbox(lines=2, label=...) |
Text input/output |
gr.Number(value=1.0) |
Numeric input/output |
gr.Slider(0, 1, 0.5) |
Range input (min, max, default) |
gr.Dropdown(choices=[...]) |
Single choice |
gr.Checkboxgroup(choices=[...]) |
Multiple choices |
gr.Radio(choices=[...]) |
Exclusive choice |
gr.Checkbox(label=...) |
Boolean |
gr.Image(type="pil") |
Image in/out (type= = pil/numpy/filepath) |
gr.Audio(type="numpy") |
Audio in/out |
gr.Video() |
Video in/out |
gr.File(file_types=[".csv"]) |
Arbitrary file |
gr.Dataframe(headers=[...]) |
Tabular in/out |
gr.HTML("<b>…</b>") / gr.Markdown("…") |
Display-only content |
gr.Label() |
Model output: labels + confidence |
gr.JSON() |
JSON in/out |
gr.Button("Run"), gr.ClearButton(...), gr.DuplicateButton(...) |
Actions |
Events — wiring Blocks¶
| Event | Fires when |
|---|---|
btn.click(fn, inputs=[...], outputs=[...]) |
Button clicked |
box.submit(fn, ...) |
Enter pressed in a textbox |
box.change(fn, ...) |
Component value changes |
box.input(fn, ...) |
While the user types (every keystroke) |
img.upload(fn, ...) / img.clear(fn, ...) |
File uploaded / cleared |
demo.load(fn, ...) |
Page loads (prefill) |
state.change(...) |
Session state changes |
every param |
gr.Timer/interval tasks (e.g. timer.tick(fn, every=5)) |
Common Operations¶
Interface with typed components¶
import gradio as gr
def predict(text, top_k, model):
# ... run inference
return {"label": "pos", "confidence": 0.93, "text": text}
gr.Interface(
fn=predict,
inputs=[
gr.Textbox(label="Review"),
gr.Slider(1, 10, 5, step=1, label="Top K"),
gr.Dropdown(["bert", "roberta"], value="bert", label="Model"),
],
outputs=gr.Label(label="Prediction"), # renders label + confidence
title="Sentiment Demo",
description="Classify a review with a fine-tuned transformer.",
).launch()
File handling — uploads and downloads¶
def summarize(file, question):
df = pd.read_csv(file.name) # temp file path from gr.File
return f"{len(df)} rows", "summary.csv" # second output = downloadable
gr.Interface(
fn=summarize,
inputs=[gr.File(file_types=[".csv"], label="Dataset"),
gr.Textbox(label="Question")],
outputs=["text", gr.File(label="Download")],
)
Image in → image out¶
def segment(img): # img is a PIL Image (type="pil")
mask = run_segmentation(img)
return mask # returned as image output
gr.Interface(fn=segment,
inputs=gr.Image(type="pil"),
outputs=gr.Image(type="pil"),
examples=["cat.jpg", "street.jpg"])
Practical Examples¶
Example 1 — Blocks app: model zoo with tabs¶
import joblib
import gradio as gr
models = {name: joblib.load(f"models/{name}.joblib")
for name in ["rf_v1", "lr_v1", "xgb_v1"]}
def predict(features, model_name):
row = [features]
prob = float(models[model_name].predict_proba(row)[0, 1])
return {"churn": prob, "retain": 1 - prob}
def show_about():
return gr.Markdown("Demo: churn prediction for three models.")
with gr.Blocks(title="Churn Zoo") as demo:
gr.Markdown("# Compare churn models")
model_name = gr.Dropdown(list(models), value="rf_v1", label="Model")
features = gr.Dataframe(headers=["tenure", "charges", "calls"],
datatype=["number"] * 3, value=[[12, 89.5, 3]])
run = gr.Button("Predict", variant="primary")
out = gr.Label(label="Probability")
run.click(predict, inputs=[features, model_name], outputs=out)
with gr.Tab("About"):
gr.Markdown("Fine-tuned on 2025 churn data. For demo only.")
demo.launch()
Example 2 — Chat demo with streaming¶
import gradio as gr
def chat(message, history):
history = history or []
response = call_llm(message, history) # or use openai client
return response # + history handling via gr.ChatInterface state
gr.ChatInterface(
fn=chat,
chatbot=gr.Chatbot(height=400),
textbox=gr.Textbox(placeholder="Ask anything...", container=False),
title="Doc Assistant",
).launch()
Example 3 — File → prediction with download¶
import pandas as pd
import gradio as gr
model = joblib.load("churn_model.joblib")
def predict_batch(file):
df = pd.read_csv(file.name)
df["prob_churn"] = model.predict_proba(df)[:, 1]
out = "predictions.csv"
df.to_csv(out, index=False)
return f"Predicted {len(df)} rows.", out
gr.Interface(
fn=predict_batch,
inputs=gr.File(file_types=[".csv"]),
outputs=[gr.Textbox(label="Status"), gr.File(label="Download")],
).launch()
Common Patterns¶
Model loading once (Blocks)¶
import joblib
import gradio as gr
# module scope = loaded once, shared by all requests
model = joblib.load("model.joblib")
with gr.Blocks() as demo:
inp = gr.Textbox(label="Text")
out = gr.Label(label="Prediction")
inp.submit(lambda t: model.predict([t])[0], inp, out)
demo.launch()
Live updates (change) — show intermediate results¶
def show_scale(value): # e.g. risk buckets as the slider moves
buckets = ["low", "medium", "high"]
idx = min(int(value * 10) // 4, 2)
return gr.Label(buckets[idx]) # returning a component value updates it
slider.change(show_scale, inputs=slider, outputs=bucket_label)
Queue for long inference¶
Heavy jobs go through the queue so requests don't time out and memory stays bounded.
Deployment — Hugging Face Spaces¶
# Local: create a public/private share link for a quick demo (no server)
demo.launch(share=True) # temporary https://xxx.gradio.live link
Space deployment:
1. Create a Space at https://huggingface.co/new-space → SDK: Gradio, hardware (CPU or GPU).
2. Push a repo with app.py + requirements.txt:
git clone https://huggingface.co/spaces/your-name/app-name
cp app.py requirements.txt app-name/
cd app-name && git add . && git commit -m "Initial demo" && git push
huggingface.co/spaces/your-name/app-name; settings allow secrets (HF Tokens, API keys) via Space secrets.
Best Practices¶
- Use
gr.Interfacefor one function,gr.Blocksonly when you need layout/events. - Load models once at module scope — never inside the event handler.
- Return component objects (e.g.
gr.Label(...)) from event handlers to update specific parts of the UI. - Add
examples=— one-click demo inputs make a demo instantly usable. - Use
type="pil"/"numpy"deliberately: transformers wantpil, torchvision transforms wantpil, raw CV code often wantsnumpy. - Keep inference behind
demo.queue()for production-ish sharing. - Add
gr.DuplicateButton()on Spaces so visitors can fork your demo. - Call
demo.launch()only underif __name__ == "__main__":so imports stay testable.
Common Mistakes¶
| Mistake | Fix |
|---|---|
| Loading the model inside the click handler (slow, reloads per request) | Load at module scope |
Returning a value instead of a component to update a change event |
Return the new component state (e.g. gr.Label(...)) |
Passing a raw pd.DataFrame to inputs= without gr.Dataframe |
Match component types: declare gr.Dataframe for tabular I/O |
gr.File gives a path — reading it as a string |
Use file.name for pandas/anything expecting a path |
Forgetting demo.queue() with a GPU model |
Add queue before launch() |
launch(share=True) in CI or in a Space |
Spaces launch themselves — don't call share |
| Output shape mismatch (dict vs tuple vs single) | One output per outputs entry; dict keys must match component labels |
Troubleshooting¶
| Symptom | Cause / Fix |
|---|---|
| App opens but clicking does nothing | Event not wired — check btn.click(fn, inputs=..., outputs=...) |
TypeError on component value |
Input type mismatch — gr.Image(type="pil") vs "numpy" |
| Model never loads / demo slow on first click | Load at import time, not inside fn |
| Files are temporary and deleted | Copy needed artifacts out of the temp dir inside the handler |
| Space keeps restarting | Check Space logs; OOM on big models → pick a larger GPU |
| Port already in use | demo.launch(server_port=7861) |
Related Cheat Sheets¶
- Streamlit — full data apps and dashboards
- data-app-patterns — shared app architecture guidance
- FastAPI — putting the same model behind a REST API
- python-requests — calling APIs from a demo
- pandas, NumPy — data prep in handlers
- Plotly — richer charts inside components
- JSON — exchanging structured output
- github — pushing to Spaces/Streamlit Cloud
Official Documentation¶
- Gradio docs — https://www.gradio.app/docs/
- Gradio Guides (Interface/Blocks/state) — https://www.gradio.app/guides/
- Hugging Face Spaces — https://huggingface.co/docs/hub/spaces
Last Updated¶
2026-09-06 — Initial version.