Hugging Face, End to EndLesson 30 of 75

Contents

Hugging Face, End to End

Lesson 30 of 75. Sign in to keep your place.

Module 4 · Spaces: putting something in front of people

Blocks, when Interface is no longer enough

9 min read

When to move

gr.Interface is one function, one form. Move to gr.Blocks when you need layout, several buttons doing different things, outputs that feed back into inputs, or anything conversational.

Python
import gradio as gr

with gr.Blocks(title="Transcribe and summarise") as demo:
    gr.Markdown("## Upload audio, get a summary")

    with gr.Row():
        with gr.Column(scale=1):
            audio = gr.Audio(type="filepath", label="Audio")
            lang = gr.Dropdown(["auto", "en", "hi"], value="auto", label="Language")
            go = gr.Button("Transcribe", variant="primary")
        with gr.Column(scale=2):
            text = gr.Textbox(label="Transcript", lines=10)
            summary = gr.Textbox(label="Summary", lines=4)
            sum_btn = gr.Button("Summarise this")

    go.click(fn=transcribe, inputs=[audio, lang], outputs=text)
    sum_btn.click(fn=summarise, inputs=text, outputs=summary)

demo.queue().launch()

The pattern is always the same: declare components inside the with block, then wire events with component.click(fn=..., inputs=..., outputs=...). inputs and outputs are lists of components, and Gradio passes their current values in and writes returned values back.

Chat, which is a component

Python
def respond(message, history):
    messages = history + [{"role": "user", "content": message}]
    out = pipe(messages, max_new_tokens=256)
    return out[0]["generated_text"][-1]["content"]

gr.ChatInterface(respond, type="messages").launch()

gr.ChatInterface gives you the message list, the history, retry, undo and clear. history arrives in exactly the {"role", "content"} shape that apply_chat_template wants, which is not a coincidence and saves a translation layer.

For streaming, make the function a generator and yield the accumulating string:

Python
def respond(message, history):
    partial = ""
    for chunk in stream_from_model(message, history):
        partial += chunk
        yield partial

Yield the whole string so far, not the chunk. Yielding chunks replaces the display each time and the user sees one word flickering.

State, and the mistake that leaks data between users

This is the most important paragraph in the lesson.

A Space is one process serving everybody. A module-level variable is shared by every visitor:

Python
history = []              # WRONG: one list for the whole internet

def chat(msg):
    history.append(msg)   # every user appends to the same list

One person’s conversation appears in another’s. On anything handling personal text this is a data breach with a two-line cause, and it is easy to write because it works perfectly when you are the only user.

The fix is gr.State, which is per-session:

Python
with gr.Blocks() as demo:
    session = gr.State([])          # separate list per browser session
    box = gr.Textbox()
    out = gr.Textbox()

    def chat(msg, hist):
        hist = hist + [msg]
        return "\n".join(hist), hist

    box.submit(chat, [box, session], [out, session])

The state goes in as an input and comes back as an output. It lives as long as the browser session and dies with it. gr.BrowserState persists in local storage across reloads for preferences; anything that must outlive the session belongs in the storage lesson later in this module.

Shared module-level objects are correct for exactly one thing: the model, which is read-only and should be shared.

Making the interface respond to itself

Python
mode = gr.Radio(["summarise", "translate"], value="summarise")
target = gr.Dropdown(["hi", "bn", "ta"], visible=False)

mode.change(
    fn=lambda m: gr.update(visible=(m == "translate")),
    inputs=mode, outputs=target,
)

gr.update() changes a component’s properties rather than its value: visibility, label, choices, interactivity. This is how a form stops showing fields that do not apply, which matters more for comprehension than it looks.

Progress, so the wait is legible

A ten-second handler with no feedback reads as broken. Gradio will show a spinner on its own, and you can do better in four lines:

Python
def transcribe(path, progress=gr.Progress()):
    chunks = split_audio(path)
    out = []
    for c in progress.tqdm(chunks, desc="Transcribing"):
        out.append(asr(c)["text"])
    return " ".join(out)

Add the progress parameter and Gradio wires a bar into the interface. On free hardware, where everything is slower than you would like, this is the difference between a visitor waiting and a visitor reloading, and reloading queues a second job while the first is still running, which makes the problem worse for everybody.

The queue, briefly

demo.queue() puts requests in a managed queue with position and estimated wait shown to the user. It is on by default in current versions and worth knowing about because concurrency_limit is how you stop eight simultaneous visitors from exhausting the memory of a free container. A queue that makes people wait visibly is better than a Space that crashes.

Do this now

Take a Space you have built with Interface and rewrite it in Blocks with two buttons, where the second acts on the output of the first. Then add a gr.State and check that opening the Space in two browsers keeps the two sessions apart.

Every lesson here is open. Your progress is recorded once you pass the Image Generation, in Practice exam.

Check your understanding

A chat Space keeps conversation history in a module-level list. It works perfectly in testing and, once public, users report seeing fragments of other people’s conversations. Why?

Pick the one you would defend. Your answer stays private.

Next lesson · 9 minDuplicate it, and the app becomes yours to change

Stuck on something?Ask Arjun, your AI tutor

How to cite: Addaly (2026) ‘Blocks, when Interface is no longer enough’, in Hugging Face, End to End. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/hugging-face/gradio-blocks-and-state (Accessed: 13 October 2026).