| name | gradio |
| description | Building ML demos and web UIs in Python with Gradio 6 — gr.Interface for wrapping a function, gr.Blocks for custom layouts with event listeners, gr.ChatInterface for LLM chat, streaming generator outputs, gr.State, image/audio components, queueing and concurrency, launch()/share links, and mounting into FastAPI with gr.mount_gradio_app. Use when demoing a model (image, audio, text, LLM), building a quick UI around a Python function, sharing a prototype via a public link, or hosting on Hugging Face Spaces. |
Gradio — ML demos and model UIs
Overview
Gradio wraps Python functions in web UIs: declare input/output components, Gradio builds
the frontend, the API, and (optionally) a public share link. The default UI for
[[transformers]] models and Hugging Face Spaces (manage Spaces with [[hf-cli]]). For
dashboards and multipage data tools, [[streamlit]] is usually the better fit (see below).
Verified against gradio 6.24 (Python 3.10+). Gradio 6 removed the legacy tuple chat
format and show_api/type= params — patterns below are current.
uv add gradio
uv run python app.py
Interface vs Blocks vs ChatInterface
| Class | Use for |
|---|
gr.Interface(fn, inputs, outputs) | One function, auto layout — fastest demo |
gr.Blocks() | Custom layout, multiple functions, cross-component events |
gr.ChatInterface(fn) | LLM chat — history, streaming, retry/undo built in |
import gradio as gr
def greet(name: str, intensity: int) -> str:
return "Hello " + name + "!" * intensity
demo = gr.Interface(fn=greet, inputs=[gr.Textbox(), gr.Slider(1, 10, step=1)],
outputs=gr.Textbox(label="greeting"), flagging_mode="never")
demo.launch()
Blocks: layout + event wiring
with gr.Blocks() as demo:
with gr.Row():
inp = gr.Textbox(label="input")
out = gr.Textbox(label="output")
btn = gr.Button("Run")
btn.click(fn=lambda s: s.upper(), inputs=inp, outputs=out)
inp.submit(lambda s: s.upper(), inp, out)
gr.on([btn.click, inp.submit], lambda s: s.upper(), inp, out)
demo.launch()
- Events live on components:
.click, .change, .submit, .upload, .select, and
demo.load (fires when a session opens). gr.Timer(2).tick(...) polls on an interval.
- To update component properties (not just value), return a component constructor:
return gr.Textbox(visible=False) — or gr.update(visible=False). Return a dict
{out_a: ..., out_b: gr.update(...)} to update a subset of outputs.
- Chain steps with
.then() (always runs) / .success() (only if no error):
btn.click(f1, ...).then(f2, ...).
- Raise
gr.Error("msg") for a user-visible error toast; gr.Warning/gr.Info for
non-fatal toasts.
Streaming outputs
Any handler that yields streams to the UI:
def stream_reply(prompt):
text = ""
for token in llm.stream(prompt):
text += token
yield text
btn.click(stream_reply, prompt_box, out_box)
Streaming inputs (webcam/mic): set streaming=True on gr.Audio/gr.Image and use
stream_every= on the event. Show progress in slow non-streaming fns by adding a
progress=gr.Progress() arg and calling progress(0.5, desc="..."), or
gr.Progress(track_tqdm=True).
Chat UIs
def chat(message: str, history: list[dict]):
partial = ""
for tok in llm.stream(history + [{"role": "user", "content": message}]):
partial += tok
yield partial
demo = gr.ChatInterface(fn=chat, examples=["Explain X"], save_history=True,
additional_inputs=[gr.Slider(0, 1, label="temperature")])
demo.launch()
Gradio 6: messages format is the only history format (the type= param and tuple
format are gone). multimodal=True accepts file uploads (message becomes
{"text": ..., "files": [...]}). Return a dict/component/list for rich replies.
State
- Plain globals are shared by all users — fine for read-only models, wrong for
per-user data.
gr.State(init): per-session state. Pass it in inputs and outputs; the handler
receives the value and returns the new value. Deep-copied per session, reset on reload.
gr.BrowserState(default): persists in localStorage across page reloads.
with gr.Blocks() as demo:
count = gr.State(0)
btn = gr.Button("count")
out = gr.Number()
btn.click(lambda c: (c + 1, c + 1), count, [count, out])
ML demo patterns (image/audio)
def classify(img):
preds = pipe(Image.fromarray(img))
return {p["label"]: p["score"] for p in preds}
demo = gr.Interface(classify, gr.Image(), gr.Label(num_top_classes=3),
examples=["examples/cat.jpg"])
gr.Audio(type="numpy") → (sample_rate, np.ndarray); type="filepath" → path str.
Return audio the same way.
gr.Gallery for image lists, gr.Label for classification dicts, gr.ImageEditor
for sketch/mask input, gr.Dataframe/gr.Plot for tabular/figure output.
- Load a heavy model once at module level (before
demo.launch()), not inside the
handler.
Queueing & concurrency
The request queue is always on. Defaults: each event runs with concurrency_limit=1
(one request at a time per event) — raise it for I/O-bound handlers:
btn.click(fn, inp, out, concurrency_limit=8)
demo.queue(default_concurrency_limit=4, max_size=64)
gen_a.click(f, ..., concurrency_id="gpu")
gen_b.click(g, ..., concurrency_id="gpu")
Launch options & deployment
demo.launch(
server_name="0.0.0.0", server_port=7860,
share=True,
auth=("user", "pass"),
max_file_size="10mb", allowed_paths=["/data/imgs"],
mcp_server=True,
)
Every app auto-exposes an API (see the "Use via API" footer link) callable with
gradio_client. Deploy on Hugging Face Spaces by pushing app.py + requirements.txt
to a Space repo ([[hf-cli]]). Note: Gradio 6 replaced show_api=False with
footer_links=[...], and per-event api_name=False hides an endpoint.
Mounting into FastAPI
from fastapi import FastAPI
import gradio as gr
app = FastAPI()
with gr.Blocks() as demo:
...
app = gr.mount_gradio_app(app, demo, path="/gradio")
Use this to add a demo UI to an existing [[fastapi]] service; don't call demo.launch()
when mounting. A handler can take a request: gr.Request arg to read headers/cookies.
Gradio vs Streamlit
Gradio is function-first: you wrap fn(inputs) -> outputs and get a UI + API +
share link — ideal for model demos, quick prototypes, and Spaces. [[streamlit]] is
script-first: the whole script reruns per interaction — ideal for dashboards,
multipage internal tools, and dataframe-heavy exploration with fine layout control.
UI around one model/function → Gradio; interactive report/tool → Streamlit.
Gotchas
- Gradio 6 breaking changes: chat history is messages-format only (no
type= on
Chatbot/ChatInterface); show_api → footer_links; check the migration guide
before copying pre-6 snippets.
- Yield cumulative values when streaming — yielding only the delta makes the output
flicker with fragments.
- Mutable globals = shared across users. Per-user data goes in
gr.State.
- GPU handlers with
concurrency_limit > 1 will OOM; serialize them with a shared
concurrency_id instead.
share=True exposes your machine through a public tunnel — never with
allowed_paths pointing at sensitive dirs.
- Blocking startup:
demo.launch() blocks; in notebooks it embeds inline, use
demo.close() to free the port.
Related
Sibling UI framework: [[streamlit]] (dashboards, data tools). Models from
[[transformers]], Spaces/Hub via [[hf-cli]]; embed in a [[fastapi]] backend with
gr.mount_gradio_app.