Skip to content

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

pip install gradio          # or: uv add gradio
python app.py               # local app at http://127.0.0.1:7860

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

demo.queue(max_size=32, default_concurrency_limit=4)   # before launch
demo.launch()

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
3. Space rebuilds and hosts at huggingface.co/spaces/your-name/app-name; settings allow secrets (HF Tokens, API keys) via Space secrets.

Best Practices

  • Use gr.Interface for one function, gr.Blocks only 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 want pil, torchvision transforms want pil, raw CV code often wants numpy.
  • Keep inference behind demo.queue() for production-ish sharing.
  • Add gr.DuplicateButton() on Spaces so visitors can fork your demo.
  • Call demo.launch() only under if __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)

Official Documentation

Last Updated

2026-09-06 — Initial version.