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...
Installs into .claude/skills of the current project.
Are you the author of Gradio?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/stanfish06-gradio)
---
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.
```bash
uv add gradio
uv run python app.py # or: gradio app.py (dev mode with auto-reload)
```
## 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 |
```python
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
```python
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) # Enter key, same handler
# one handler for several triggers:
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 `yield`s streams to the UI:
```python
def stream_reply(prompt):
text = ""
for token in llm.stream(prompt):
text += token
yield text # yield the FULL value so far, not the delta
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
```python
def chat(message: str, history: list[dict]): # history = OpenAI-style messages:
# [{"role": "user", "content": "hi"}, {"role": "assistant", "content": "hello"}]
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.
```python
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)
```python
def classify(img): # gr.Image default type="numpy"; also "pil"/"filepath"
preds = pipe(Image.fromarray(img)) # e.g. a transformers pipeline
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"]) # cached example gallery
```
- `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:
```python
btn.click(fn, inp, out, concurrency_limit=8) # 8 concurrent for this event
demo.queue(default_concurrency_limit=4, max_size=64) # app-wide default + queue cap
gen_a.click(f, ..., concurrency_id="gpu") # share one limit across events
gen_b.click(g, ..., concurrency_id="gpu")
```
## Launch options & deployment
```python
demo.launch(
server_name="0.0.0.0", server_port=7860, # bind for Docker/remote access
share=True, # free *.gradio.live public tunnel (72h)
auth=("user", "pass"), # basic auth; or a callable
max_file_size="10mb", allowed_paths=["/data/imgs"],
mcp_server=True, # expose handlers as MCP tools
)
```
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
```python
from fastapi import FastAPI
import gradio as gr
app = FastAPI()
with gr.Blocks() as demo:
...
app = gr.mount_gradio_app(app, demo, path="/gradio")
# uvicorn main:app --host 0.0.0.0 --port 8000 → UI at /gradio, your routes elsewhere
```
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`.