Skip to main content
The Blocks class gives you fine-grained control over your Gradio application. Unlike Interface, which automatically creates a simple layout, Blocks lets you customize the layout, define multiple data flows, and create complex interactions between components.

Understanding Blocks structure

Here’s a simple example that shows the basic structure of a Blocks app:
import gradio as gr

def greet(name):
    return f"Hello {name}!"

with gr.Blocks() as demo:
    name = gr.Textbox(label="Name")
    output = gr.Textbox(label="Output")
    greet_btn = gr.Button("Greet")
    greet_btn.click(fn=greet, inputs=name, outputs=output)

demo.launch()
Let’s break down what’s happening:
1

Create the Blocks context

The with gr.Blocks() as demo: clause creates a context where you define your app. All components created inside this context are automatically added to the app.
2

Add components

Components like Textbox and Button are created inside the context. These are the same components used in Interface, but here you have full control over their placement and configuration.
3

Define event listeners

The click() method attaches an event listener to the button. This defines the data flow: when the button is clicked, the greet function runs with name as input and updates output with the result.

Using decorator syntax

You can also attach event listeners using decorators. Skip the fn argument and assign inputs and outputs directly:
import gradio as gr

with gr.Blocks() as demo:
    name = gr.Textbox(label="Name")
    output = gr.Textbox(label="Output")
    greet_btn = gr.Button("Greet")
    
    @greet_btn.click(inputs=name, outputs=output)
    def greet(name):
        return f"Hello {name}!"

demo.launch()

Event listeners and interactivity

Gradio automatically determines which components should be interactive based on how they’re used:
  • Input components: Any component that serves as an input to an event listener is made interactive
  • Output-only components: Components that only serve as outputs are non-interactive
  • Override behavior: You can explicitly control interactivity with the interactive parameter
# Make an output component interactive
output = gr.Textbox(label="Output", interactive=True)
If a component is neither an input nor an output:
  • With a default value: rendered non-interactive (displaying content)
  • Without a default value: rendered interactive
You can always override this with interactive=True or interactive=False.

Types of event listeners

Different components support different event listeners. Here’s an example using the change() event:
import gradio as gr

def welcome(name):
    return f"Welcome to Gradio, {name}!"

with gr.Blocks() as demo:
    gr.Markdown("""
    # Hello World!
    Start typing below to see the output.
    """)
    inp = gr.Textbox(placeholder="What is your name?")
    out = gr.Textbox()
    inp.change(welcome, inp, out)

demo.launch()
Instead of requiring a button click, this function triggers whenever you type in the textbox. The change() event listener fires when the component’s value changes.

Common event listeners

  • .click(): Triggered when a button or clickable component is clicked
  • .change(): Triggered when a component’s value changes
  • .submit(): Triggered when Enter is pressed in an input component
  • .input(): Triggered on every keystroke in a text input
  • .select(): Triggered when text or an item is selected
  • .upload(): Triggered when a file is uploaded
  • .play(): Triggered when media playback starts
Check the component documentation to see which event listeners are available for each component.

Multiple data flows

Unlike Interface, Blocks apps can have multiple data flows connecting various components. Components can serve as both inputs and outputs:
import gradio as gr

with gr.Blocks() as demo:
    with gr.Row():
        num1 = gr.Number()
        num2 = gr.Number()
    
    with gr.Row():
        btn1 = gr.Button("Copy →")
        btn2 = gr.Button("← Copy")
    
    btn1.click(lambda x: x, num1, num2)
    btn2.click(lambda x: x, num2, num1)

demo.launch()
In this example, num1 can act as input to num2, and vice versa.

Function inputs: list vs set

When you have multiple input components, you can pass their values to your function in two ways:

Option 1: List of arguments

def add(num1, num2):
    return num1 + num2

add_btn.click(fn=add, inputs=[a, b], outputs=output)
The values are passed as separate arguments in the order they appear in the inputs list.

Option 2: Dictionary (using a set)

def sub(data):
    return data[a] - data[b]

sub_btn.click(fn=sub, inputs={a, b}, outputs=output)
When you pass inputs as a set (using curly brackets {}), your function receives a single dictionary where keys are the input components and values are their current values.
Option 2 is particularly useful for functions with many input components, as it makes the function signature cleaner and easier to manage.

Function returns: list vs dict

Similarly, you can return values for multiple outputs in two ways:

Option 1: Return a list

with gr.Blocks() as demo:
    food_box = gr.Number(value=10, label="Food Count")
    status_box = gr.Textbox()
    
    def eat(food):
        if food > 0:
            return food - 1, "full"
        else:
            return 0, "hungry"
    
    gr.Button("Eat").click(
        fn=eat,
        inputs=food_box,
        outputs=[food_box, status_box]
    )
Return values in the same order as the outputs list.
For a single output component, return the value directly, not as a single-item list. Gradio cannot distinguish whether the outer list is part of your return value or a container.

Option 2: Return a dictionary

with gr.Blocks() as demo:
    food_box = gr.Number(value=10, label="Food Count")
    status_box = gr.Textbox()
    
    def eat(food):
        if food > 0:
            return {food_box: food - 1, status_box: "full"}
        else:
            return {status_box: "hungry"}
    
    gr.Button("Eat").click(
        fn=eat,
        inputs=food_box,
        outputs=[food_box, status_box]
    )
With dictionary returns, you can:
  • Update only specific outputs (notice how we skip food_box when food is 0)
  • Update outputs in any order
  • Conditionally update different sets of outputs
Even with dictionary returns, you must still specify all possible outputs in the event listener’s outputs parameter.

Updating component configurations

You can update not just a component’s value, but also its configuration (like visibility, label, or interactivity):
import gradio as gr

def sentence_builder(quantity, animal):
    return f"The {quantity} {animal}s went to the market."

with gr.Blocks() as demo:
    quantity = gr.Slider(
        2, 20, value=4, step=1, 
        label="Quantity", interactive=True
    )
    animal = gr.Textbox(
        label="Animal", placeholder="Enter an animal"
    )
    output = gr.Textbox(label="Output", interactive=False)
    
    def update_visibility(quantity):
        if quantity < 10:
            return gr.Textbox(visible=True, value="")
        return gr.Textbox(visible=False, value="Hidden!")
    
    quantity.change(update_visibility, quantity, animal)
    gr.Button("Submit").click(sentence_builder, [quantity, animal], output)

demo.launch()
Return a new component instance with the properties you want to change. Any properties you don’t set will preserve their previous values.

Not changing a component’s value

Sometimes you want to leave a component’s value unchanged. Use gr.skip() for this:
import gradio as gr

with gr.Blocks() as demo:
    textbox = gr.Textbox()
    button1 = gr.Button("Clear")
    button2 = gr.Button("Skip")
    
    button1.click(lambda: None, None, textbox)  # Clears the textbox
    button2.click(lambda: gr.skip(), None, textbox)  # Leaves value unchanged

demo.launch()
Returning None typically resets a component to its empty state, while gr.skip() preserves the current value. If you have multiple outputs and want to leave all unchanged, return a single gr.skip() instead of a tuple of skips.

Running events consecutively

Use the .then() method to run events in sequence:
import gradio as gr
import time

def add_message(history, message):
    history.append((message, None))
    return history, ""

def respond(history):
    time.sleep(1)  # Simulate processing
    response = "Computer says: " + history[-1][0].upper()
    history[-1] = (history[-1][0], response)
    return history

with gr.Blocks() as demo:
    chatbot = gr.Chatbot()
    msg = gr.Textbox()
    send = gr.Button("Send")
    
    send.click(add_message, [chatbot, msg], [chatbot, msg]).then(
        respond, chatbot, chatbot
    )

demo.launch()
The .then() method executes regardless of whether the previous event raised errors.

Conditional chaining

  • .success(): Only runs if the previous event completed without errors
  • .failure(): Only runs if the previous event raised an error
These are useful for error handling workflows:
submit_btn.click(process_data, inputs, outputs).success(
    show_success_message, None, status
).failure(
    show_error_message, None, status
)

Binding multiple triggers to a function

Use gr.on() to bind multiple triggers to the same function:
import gradio as gr

with gr.Blocks() as demo:
    input_text = gr.Textbox()
    output = gr.Textbox()
    submit = gr.Button("Submit")
    
    # Trigger on both button click and Enter key
    gr.on(
        triggers=[submit.click, input_text.submit],
        fn=lambda x: x.upper(),
        inputs=input_text,
        outputs=output
    )

demo.launch()

Decorator syntax with gr.on

@gr.on([submit.click, input_text.submit], inputs=input_text, outputs=output)
def process_text(text):
    return text.upper()

Creating “live” events

Omit the triggers parameter to automatically bind to all change events of input components:
import gradio as gr

with gr.Blocks() as demo:
    first_name = gr.Textbox(label="First Name")
    last_name = gr.Textbox(label="Last Name")
    full_name = gr.Textbox(label="Full Name")
    
    # Automatically triggers when either input changes
    @gr.on(inputs=[first_name, last_name], outputs=full_name)
    def combine_names(first, last):
        return f"{first} {last}"

demo.launch()

Binding component values directly

For simple cases where a component’s value should always be a function of other components, use this shorthand:
with gr.Blocks() as demo:
    num1 = gr.Number()
    num2 = gr.Number()
    product = gr.Number(lambda a, b: a * b, inputs=[num1, num2])
This is equivalent to:
with gr.Blocks() as demo:
    num1 = gr.Number()
    num2 = gr.Number()
    product = gr.Number()
    
    gr.on(
        [num1.change, num2.change, demo.load],
        lambda a, b: a * b,
        inputs=[num1, num2],
        outputs=product
    )

Next steps

Now that you understand how to create Blocks and attach event listeners, you can: