gradio · difficulty ◆◆
gr.Label — the classifier output that draws its own confidence bars
Return {class: score}; gr.Label sorts it, cuts it to the top N and paints the bars — the rest of the looks are not yours to control.
gr.Label quietly deletes every class after the third, and it will happily draw a 250%-wide bar if you hand it raw logits.
$ gr.Label()What it does
gr.Label takes one dict of {class_name: score} and renders a headline (the top class) plus one row per class: a native <meter> bar, the class name, and a whole-number percentage. Sorting happens server-side in postprocess(), which also slices to num_top_classes if you set it and returns LabelData(label=..., confidences=[...]) to the browser. Pass a str, int or float instead and the bars disappear — you just get the value as large text.
Why it matters
Classification output is the one piece of a demo nobody wants to hand-roll: sort the scores, name them, size the bars, keep it readable at 3 classes and at 1,000. gr.Label does all of it from two lines, and it puts aria-valuenow on every bar so a screen reader announces the confidence. Pair it with gr.Image or gr.Textbox and the classic classifier demo is done before the model finishes loading.
Example
$ import gradio as gr
def predict_intent(message):
# stand-in for a real classifier — swap in model.predict_proba(...)[0]
return {"shipping": 0.7212, "billing": 0.1506, "returns": 0.0789, "cancel": 0.0493}
with gr.Blocks(title="Support ticket router") as demo:
gr.Markdown("### Support ticket router")
msg = gr.Textbox(value="Where is order #10482?", label="customer message", lines=2)
run = gr.Button("Route ticket", variant="primary")
top = gr.Label(num_top_classes=3, label="predicted intent")
run.click(predict_intent, inputs=msg, outputs=top)
demo.launch()* Running on local URL: http://127.0.0.1:7860
* To create a public link, set `share=True` in `launch()`.
Rendered UI (headless Chromium against this exact app, gradio 6.18.0 / Python 3.12):
- Markdown heading, then Textbox "customer message" (2 lines) and primary Button "Route ticket"
- below: Label "predicted intent" -> <h2 data-testid="label-output-value">shipping</h2>
- three <button class="confidence-set"> rows, one <meter> each:
shipping 72% value 0.7212, style="width: 72.12%"
billing 15% value 0.1506, style="width: 15.06%"
returns 8% value 0.0789, style="width: 7.89%"
- "cancel" (0.0493) is nowhere in the DOM — num_top_classes=3 sliced it off server-sideVerified by launching this exact snippet and clicking "Route ticket" in Chromium. The click POSTs {"data":["Where is order #10482?"],"fn_index":1,...}; the returned dict goes through the component's postprocess(), which sorts it and promotes the top key to the headline. The scores are a hard-coded stand-in — drop your own predict_proba() in the same slot.
$ import gradio as gr
def predict(message):
return {"shipping": 0.7212, "billing": 0.1506, "returns": 0.0789, "cancel": 0.0493}
def on_select(evt: gr.SelectData):
print(f"[select] index={evt.index} value={evt.value}")
return f"queued: {evt.value}"
with gr.Blocks(title="Click a bar") as demo:
msg = gr.Textbox(value="Where is order #10482?", label="customer message")
run = gr.Button("Route ticket")
top = gr.Label(num_top_classes=3, label="predicted intent")
picked = gr.Textbox(label="agent queue")
run.click(predict, inputs=msg, outputs=top)
top.select(on_select, inputs=None, outputs=picked)
demo.launch()* Running on local URL: http://127.0.0.1:7860
* To create a public link, set `share=True` in `launch()`.
[select] index=1 value=billing
Rendered UI and server output, verified on 6.18.0:
- after "Route ticket", every confidence row carries the class "confidence-set group ... selectable" — the bars are real <button> elements
- clicking the second bar sends {"data":[],"event_data":{"index":1,"value":"billing"},"fn_index":2,...}
- the handler prints [select] index=1 value=billing and Textbox "agent queue" reads "queued: billing"End-to-end verified: I clicked the second bar in Chromium and read the handler line off the server stdout. The payload has exactly two keys — index (0-based, matching display order) and value (the class string). Add the .select listener and /config flips the component's "_selectable" prop to true; without it the bars are inert.
$ import gradio as gr
def estimate_price(sqft, beds):
return 118.4 * sqft + 9000 * beds # a float, not a dict
with gr.Blocks(title="Price regressor") as demo:
sqft = gr.Number(value=1200, label="square feet")
beds = gr.Slider(1, 5, value=3, step=1, label="bedrooms")
run = gr.Button("Estimate price")
price = gr.Label(label="estimated price (USD)", color="#009245")
run.click(estimate_price, inputs=[sqft, beds], outputs=price)
demo.launch()* Running on local URL: http://127.0.0.1:7860
* To create a public link, set `share=True` in `launch()`.
Rendered UI (verified on 6.18.0):
- Number "square feet" (1200), Slider "bedrooms" (1-5, step 1, value 3), Button "Estimate price"
- Label "estimated price (USD)" renders ONE element:
<h2 data-testid="label-output-value" style="background-color: rgb(0, 146, 69);">169080.0</h2>
- <meter> element count: 0 — no scores, no bars
- postprocess() wrapped the float as LabelData(label="169080.0"): str(value), so the trailing .0 survivesSame component, three shapes: dict -> headline plus bars, float/str -> headline only, {} or None -> empty block. 118.4*1200 + 9000*3 = 169080.0 exactly, and that string is what the page shows — no thousands separator, no format hook. Read straight from the heading's textContent and style attribute.
Common flags
- num_top_classes=3
- How many classes reach the browser. The slice happens after sorting, so the dropped scores never leave Python — no 'and 3 more' affordance.
- value={...}
- A dict renders bars; a str/int/float renders only the headline (LabelData with confidences=null); {} or None renders an empty block.
- color="#009245"
- Fills the background of the headline <h2> and nothing else — verified as style="background-color: rgb(0, 146, 69);". Invisible once show_heading=False and bars are present.
- show_heading=False
- Hides the big headline so only the bars show — but ignored when there are no confidences, because a lone number still needs its label.
- label="predicted intent"
- Block caption above the component; inside gr.Interface it becomes the output parameter name shown beside the result.
- .select
- Attach a listener and each row becomes a clickable <button class="confidence-set selectable">; with no listener the component reports _selectable: false in /config.
- scale / min_width
- Layout only: how the block shares space in a Row and when it wraps. Defaults min_width=160, so a Label next to two inputs happily wraps on mobile.
History
A deprecated shim that outlived its module
By gradio 3.50.2 the public name was a three-parameter re-export: class Label(components.Label) lived in gradio/outputs.py and its __init__ called warn_outputs_deprecation(). The real implementation already sat in gradio/components/label.py (7,849 bytes at that tag) — and that is still where it lives. 4.0.0 kept the module and gave the component the full Component surface: color, elem_id, scale, render, later key and inputs.
show_heading took until 5.7.0
Hiding the headline became possible in 5.7.0 via PR #9987 by @hannahblair, per the repo CHANGELOG. I diffed the file at both ends of the range: show_heading is absent from gradio/components/label.py at 5.0.0 and present at 5.20.0. preserved_by_key — the 'keep this value across page reloads' switch — appears between those same two tags (absent at 5.20.0, present at 5.30.0).
Fun facts
Pros & cons
pros
- + Two lines from model scores to a finished result block: sorting, the top-N cut, bars, percentages and screen-reader semantics all come free.
- + Accepts three value shapes — dict for classification, str for a single class, float/int for regression — so the output type can change without swapping the component.
- + Attach .select and each bar turns into a clickable drill-down, which converts a classifier into a filter UI with zero custom JavaScript.
cons
- − The top-N cut is silent: a four-class model with num_top_classes=3 shows three rows and never hints that a fourth class existed.
- − No clamping: logits produce >100% bars that overflow the block, so you must normalise yourself.
- − The display rounds with Math.round(conf*100), so 0.9965 and 1.0 look identical — awkward if your demo is about uncertainty.
Takeaways
- 1Return {class_name: score}; the highest-scoring key becomes the headline and the bars sort themselves.
- 2Normalise before you return — softmax or divide by the sum — because there is no clamping and '250%' is a real render.
- 3Set num_top_classes explicitly; leave it None only if you want every class the model emitted drawn as a row.
- 4Attach .select if the bars should be clickable: the payload is exactly {'index': i, 'value': 'label'}.
- 5If a probability must not read as certainty, format it yourself first — the browser rounds to whole percent.