Designing UI with Gradio

Quick Start with gr.Interface

Let's start from a simplest demo.

1
2
3
4
5
6
7
import gradio as gr

def your_function(input_text):
return "For example, a translation model's output taking" + input_text + "as input"

demo = gr.Interface(fn=your_function, inputs="text", outputs="text")
demo.launch()

gr.Interface allows us to define a UI for your_function. The type of inputs and outputs must be predefined. Gradio allows 'text' 'image' 'number' 'audio' 'video' for both inputs and outputs.

Other types that might be useful: Input-only:

  • 'slider' - inputting values within a specified range using a slider
  • 'checkbox' - boolean input
  • 'radio' - single selection input
  • 'dropdown' - selecting from a dropdown menu

Output-only:

  • 'label' - displaying labels or classification results
  • 'plot' - displaying charts (e.g., Matplotlib or Plotly plots)

Multiple inputs and outputs by putting types into a list, for example:

1
2
3
4
5
6
7
import gradio as gr

def your_function(input_text, language):
return "A" + language + "translation model's output taking" + input_text + "as input"

demo = gr.Interface(fn=your_function, inputs=["text","text"], outputs="text")
demo.launch()

After running the python script, we can Ctrl + Click http://localhost:7860 from your terminal, then you can see a interface in our browser like this:

Now we have everything for building a clear-enough UI for any target function. Let me give some example:

1
2
3
4
gr.Interface(fn=your_function, inputs="image", outputs="label") # A classification model
gr.Interface(fn=your_function, inputs="text", outputs="image") # A Text-to-Image generation model
gr.Interface(fn=your_function, inputs=["slider","slider","image"], outputs="image") # Segment Anything (SAM) with point prompt
gr.Interface(fn=your_function, inputs=["image","video"], outputs="video") # SAM2 with mask prompt

The only problem now is layouts, right? When the UI is complex, not having layouts can be uncomfortable. In fact, there are many other functions that cannot be implemented with gr.Interface, such as getting information about mouse clicks, triggering inference in a way other than a button, and generating a different number of output boxes depending on the input. When you find that the simple Interface function no longer meets your needs, you may want to try gr.Blocks.

Building Better Interfaces with gr.Blocks

One of the most noticeable changes when using gr.Blocks is the ability to design the layout. We can now have more control over how the UI is arranged. Let's first implement the example we just saw with gr.Interface using gr.Blocks.

A Simple Gradio Demo with gr.Blocks

Here is a simple demo includes a text input, an output box, a submit button, and a clear button.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import gradio as gr

def your_function(name):
return "Hello " + name + "!"

def clear_all():
return ["", ""]

with gr.Blocks() as demo:
name = gr.Textbox(label="Name")
output = gr.Textbox(label="Output Box")
submit_btn = gr.Button("Submit")
clear_btn = gr.Button("Clear")
submit_btn.click(fn=your_function, inputs=name, outputs=output)
clear_btn.click(fn=clear_all, inputs=None, outputs=[name, output])

demo.launch()

The UI looks like this:

Does the layout look a bit different? Well, the basic functionality is the same as in the previous example. In this case, we define a gr.Blocks, which is like taking a sheet of A4 paper when you start working on an assignment. Then we create two gr.Textbox and two gr.Button on the paper, and they are automatically arranged within this block area.

Layout

You can design your layout using gr.Column and gr.Row to make it look like the UIs you see on Hugging Face. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import gradio as gr

with gr.Blocks() as demo:
with gr.Row():
text1 = gr.Textbox(label="t1")
slider2 = gr.Textbox(label="s2")
drop3 = gr.Dropdown(["a", "b", "c"], label="d3")
with gr.Row():
with gr.Column(scale=1, min_width=300):
text1 = gr.Textbox(label="prompt 1")
text2 = gr.Textbox(label="prompt 2")
inbtw = gr.Button("Between")
text4 = gr.Textbox(label="prompt 1")
text5 = gr.Textbox(label="prompt 2")
with gr.Column(scale=2, min_width=300):
img1 = gr.Image("images/cheetah.jpg")
btn = gr.Button("Go")

demo.launch()

Like the following code, you can divide gr.Blocks into multiple horizontal sub-areas, and then further divide one of these sub-areas into multiple vertical sub-areas.

1
2
3
4
5
6
7
8
with gr.Blocks() as demo:
with gr.Row():
...
with gr.Row():
with gr.Column():
...
with gr.Column():
...

To limit the shape of a block, try:

1
with gr.Column(scale=2, min_width=300)

Function Trigger Methods

Although my favorite is Button, there are other ways to trigger your function. You can try using an event listener or triggering the function by pressing ENTER. The former can monitor changes in the input box in real-time, while the latter replaces the button with a keyboard key.

1
2
3
4
5
6
7
8
9
10
11
# Button  
btn = gr.Button("Submit")
submit_btn.click(fn=your_function, inputs=input, outputs=output)

# Event listener
inp = gr.Textbox()
inp.change(welcome, inp, out)

# 'ENTER'
inp = gr.Textbox(label="Input")
inp.submit(greet, name, [output, trigger])

You can also set multiple triggers using gr.Blocks().

1
gr.on(triggers=[name.submit, greet_btn.click], fn=greet, inputs=name, outputs=output).then(clear_name, outputs=[name])

You may use .then(clear_name, outputs=[name]) to call a second function after 'greet' has been triggered. Here, outputs=[name] means that the return value will update the "name" input box. The .then() operation can actually be written inside greet, making greet look cleaner, but it is not mandatory.

Controling the visibility of a sub-block

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import gradio as gr

with gr.Blocks() as demo:
name_box = gr.Textbox(label="Name")
age_box = gr.Number(label="Age", minimum=0, maximum=100)
symptoms_box = gr.CheckboxGroup(["Cough", "Fever", "Runny Nose"])
submit_btn = gr.Button("Submit")

with gr.Column(visible=False) as output_col:
diagnosis_box = gr.Textbox(label="Diagnosis")
patient_summary_box = gr.Textbox(label="Patient Summary")

def submit(name, age, symptoms):
return {
submit_btn: gr.Button(visible=False),
output_col: gr.Column(visible=True),
diagnosis_box: "covid" if "Cough" in symptoms else "flu",
patient_summary_box: f"{name}, {age} y/o",
}

submit_btn.click(
submit,
[name_box, age_box, symptoms_box],
[submit_btn, diagnosis_box, patient_summary_box, output_col],
)

demo.launch()

In this example, clicking the submit_btn triggers the submit function. The change in visibility is specified as one of the outputs.
You can modify the visibility of individual components or an entire block. If it applies to a block, you need to define a variable for it when creating the block.

Determine the Output Boxes Based on Input

If you are unsure how many outputs will be generated before entering your inputs, you can use gr.render().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import gradio as gr

with gr.Blocks() as demo:
input_text = gr.Textbox(label="Input")

@gr.render(inputs=input_text)
def show_split(text):
if len(text) == 0:
gr.Markdown("## No Input Provided")
else:
for letter in text:
gr.Textbox(letter)

demo.launch()

@gr.render(inputs=input_text) is actually listening to the change in input_text if there is no trigger defined. You can use this listening mechanism to create some interesting designs. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import gradio as gr

with gr.Blocks() as demo:
text_count = gr.State(1)
add_btn = gr.Button("Add Box")
add_btn.click(lambda x: x + 1, text_count, text_count)

@gr.render(inputs=text_count)
def render_count(count):
boxes = []
for i in range(count):
box = gr.Textbox(key=i, label=f"Box {i}")
boxes.append(box)

def merge(*args):
return " ".join(args)

merge_btn.click(merge, boxes, output)

merge_btn = gr.Button("Merge")
output = gr.Textbox(label="Merged Output")

demo.launch()

Here, gr.State() allows you to listen to events in the state variable. If you are adding a new box through gr.render() but have already input something into the existing boxes, remember to set key= to maintain the content in the displayed boxes.

Provide Some Examples on the UI

If you want to show users how to use your UI, you can use gr.Examples() to provide pre-filled example inputs. This helps users understand how to interact with your interface more effectively.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import gradio as gr

def calculator(num1, operation, num2):
if operation == "add":
return num1 + num2
elif operation == "subtract":
return num1 - num2
elif operation == "multiply":
return num1 * num2
elif operation == "divide":
return num1 / num2

with gr.Blocks() as demo:
with gr.Row():
with gr.Column():
num_1 = gr.Number(value=4, label="Number 1")
operation = gr.Radio(["add", "subtract", "multiply", "divide"], label="Operation")
num_2 = gr.Number(value=0, label="Number 2")
submit_btn = gr.Button(value="Calculate")
with gr.Column():
result = gr.Number(label="Result")

submit_btn.click(
calculator, inputs=[num_1, operation, num_2], outputs=[result], api_name=False
)
examples = gr.Examples(
examples=[
[5, "add", 3],
[4, "divide", 2],
[-4, "multiply", 2.5],
[0, "subtract", 1.2],
],
inputs=[num_1, operation, num_2],
label="Examples"
)
demo.launch(show_api=False)

In this example, gr.Examples() provides a set of predefined input combinations. Users can click on any example to automatically fill the input boxes with those values. This feature makes it easier for users to test different scenarios and understand how the calculator works.

Select a Point on the gr.Image or gr.Dataframe

If you want to click on an image to get the coordinates of where you clicked relative to the image, you can use gr.select() to achieve this.

1
2
3
4
5
6
7
8
9
10
def get_select_coords(img, evt: gr.SelectData):
cord_x = evt.index[0]
cord_y = evt.index[1]
return cord_x, cord_y

with gr.Blocks() as demo:
first_img = gr.Image(label="First Image")
x_cord = gr.Textbox(label="X Coordinate")
y_cord = gr.Textbox(label="Y Coordinate")
first_img.select(get_select_coords, [first_img], [x_cord, y_cord])

Similarly, gr.Dataframe is a table-format component that also supports the select method.

Summary of Useful Functions for gr.Blocks

1
2
3
4
5
6
7
8
gr.Row(scale=1, min_width=300) 
gr.Column()
gr.Column(visible=False) # Hide a block unless a trigger change the state of 'visible'
gr.select() # Returns the interaction information when interacting with a variable of a certain type
gr.Tab(("Next Page")) # Select one of the multiple Tabs that the current UI displays.
gr.Accordion("Open for More!", open=False) # A block that can be hidden
gr.render() # Generate boxes according to your inputs
gr.Examples() # Display input examples, support clicking examples to fill in the input