Building a Modern Dashboard with Gradio
Overview
This tutorial guides you through creating a professional, multi-tab Gradio application with interactive visualizations using the Iris dataset. You’ll learn how to build a dashboard with multiple views, responsive filters, and statistical analysis—all with a clean, modern design.
Note
This tutorial uses the built-in Iris dataset, so you can run it immediately without downloading external files. The architecture mirrors typical data science dashboards used in machine learning, data science, and exploratory analysis.
What You’ll Learn
By following this tutorial, you’ll understand:
How to structure a multi-tab Gradio application using
gr.Tabs()Creating responsive layouts with Gradio’s Row and Column components
Building interactive filters (dropdowns for categorical data)
Using Plotly for advanced visualizations (scatter, box, heatmap charts)
Managing callbacks to handle user interactions
Applying statistical analysis (correlation, PCA) to data
Styling visualizations with custom colors and layouts
—
1. Setup & Prerequisites
Key Libraries Overview
gradio: The web framework for building interactive dashboards
plotly: Advanced interactive visualizations
pandas: Data manipulation and analysis
numpy: Numerical computations
scikit-learn: Machine learning utilities (StandardScaler, PCA)
—
2. Step-by-Step Implementation
2.1 Imports & Data Setup
Start by importing all necessary libraries and loading your data:
import os
import gradio as gr
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
# Load the Iris dataset (built-in to Plotly)
df = px.data.iris()
Explanation
gr: Gradio framework for building the UIplotly.express: High-level API for creating interactive chartsplotly.graph_objects: Low-level API for fine-grained controlThe Iris dataset contains measurements of sepal and petal dimensions for three flower species
2.2 Define Your Design System (Theme)
Create a centralized theme dictionary to maintain consistency:
theme = {
'bg_main': '#F0F2F5', # Light grey-blue main background
'bg_card': '#FFFFFF', # White for card backgrounds
'text_primary': '#2C3E50', # Dark blue-gray for primary text
'text_secondary': '#7F8C8D', # Medium gray for secondary text
'accent': '#6C5CE7', # Purple for accents
'success': '#00B894', # Green for success/positive
'font_family': '"Segoe UI", Roboto, Helvetica, Arial, sans-serif'
}
2.3 Helper Functions
Create functions to handle data updates:
Function Overview:
These three helper functions handle all data filtering, aggregation, and visualization logic. Each is designed to be triggered by a Gradio callback when the user selects a different species.
update_overview() - Main Statistics Dashboard
def update_overview(selected_species):
"""Update overview page with KPIs and visualizations"""
dff = df[df.species == selected_species] if selected_species != 'All' else df
# KPIs
avg_sepal = f"{dff['sepal_length'].mean():.2f} cm"
avg_petal = f"{dff['petal_length'].mean():.2f} cm"
total_samples = f"{len(dff)} samples"
kpi_text = f"""
### Key Performance Indicators
**Average Sepal Length:** {avg_sepal}
**Average Petal Length:** {avg_petal}
**Total Samples:** {total_samples}
"""
# Scatter Plot
fig_scatter = px.scatter(
dff, x="sepal_length", y="petal_length", color="species", size="sepal_width",
hover_name="species", title="Sepal vs Petal Length"
)
fig_scatter.update_layout(
plot_bgcolor='rgba(0,0,0,0)', paper_bgcolor='rgba(0,0,0,0)',
font_family=theme['font_family']
)
# Bar Chart
species_stats = dff.groupby("species")[["sepal_length", "petal_length"]].mean().reset_index()
fig_bar = px.bar(
species_stats, x="species", y="sepal_length", color="species",
title="Average Sepal Length by Species"
)
fig_bar.update_layout(
plot_bgcolor='rgba(0,0,0,0)', paper_bgcolor='rgba(0,0,0,0)',
font_family=theme['font_family'], showlegend=False
)
# Table
table_html = dff.head(10)[['species', 'sepal_length', 'petal_length', 'sepal_width']].to_html(index=False)
return kpi_text, fig_scatter, fig_bar, table_html
What this function does:
Data Filtering: Checks if user selected a specific species or “All”. If “All”, uses entire dataset (150 samples); otherwise filters to 50 samples for that species
KPI Calculation: Computes mean sepal length, mean petal length, and total sample count
Scatter Plot: Visualizes relationship between sepal and petal lengths, with point size representing sepal width
Bar Chart: Shows average sepal length grouped by species
HTML Table: Displays first 10 records with key measurements
Returns: Tuple of (markdown text, scatter figure, bar figure, HTML table) to update all 4 dashboard elements
update_species_details() - Distribution Analysis
def update_species_details(selected_species):
"""Update species details page"""
if not selected_species or selected_species == 'All':
dff = df
else:
dff = df[df.species == selected_species]
if len(dff) == 0:
return px.box(title=f"No data for {selected_species}")
fig = px.box(dff, y='sepal_length', x='species',
title=f"Sepal Length Distribution: {selected_species if selected_species != 'All' else 'All Species'}")
fig.update_layout(
plot_bgcolor='rgba(0,0,0,0)', paper_bgcolor='rgba(0,0,0,0)',
font_family=theme['font_family']
)
return fig
What this function does:
Data Filtering: Filters iris data by selected species (or shows all if “All” selected)
Validation: Checks for empty data and returns empty plot if no data found
Box Plot: Creates box plot showing sepal length distribution (median, quartiles, outliers) grouped by species
Styling: Applies transparent background and custom font for consistency
update_analytics() - Advanced Analysis with PCA
def update_analytics(selected_species):
"""Perform advanced analytics with PCA"""
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
if selected_species == 'All':
dff = df.copy()
else:
dff = df[df.species == selected_species].copy()
# Prepare data for PCA
features = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
X = dff[features]
# Standardize the features
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Apply PCA
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
# Create PCA dataframe
pca_df = pd.DataFrame(
data=X_pca, columns=['PC1', 'PC2']
)
pca_df['species'] = dff['species'].values
# PCA Plot
fig_pca = px.scatter(
pca_df, x='PC1', y='PC2', color='species',
title=f"PCA Analysis (Explained Variance: {sum(pca.explained_variance_ratio_):.2%})"
)
fig_pca.update_layout(
plot_bgcolor='rgba(0,0,0,0)', paper_bgcolor='rgba(0,0,0,0)',
font_family=theme['font_family']
)
# Correlation heatmap
corr = dff[features].corr()
fig_heatmap = px.imshow(
corr, labels=dict(color="Correlation"), title="Feature Correlation",
color_continuous_scale='RdBu_r', zmin=-1, zmax=1
)
fig_heatmap.update_layout(font_family=theme['font_family'])
return fig_pca, fig_heatmap
What this function does:
Feature Selection: Uses all 4 iris measurements (sepal/petal length & width)
Standardization: Scales features to zero mean and unit variance using StandardScaler (essential for PCA)
PCA Transformation: Reduces 4-dimensional space to 2D while preserving maximum variance
PCA Scatter Plot: Visualizes iris samples in the reduced 2D space with species color coding - Shows how well species cluster together - PC1 and PC2 represent the most important directions of variance - Title shows cumulative explained variance (typically 95%+)
Correlation Heatmap: Shows correlation matrix between all 4 features - Red = positive correlation - Blue = negative correlation - Used to identify which measurements are related
Returns: Tuple of (PCA scatter figure, correlation heatmap)
Key Design Patterns:
Modular Design: Each function is independent and reusable
Consistent Styling: All plots use the theme dictionary for uniform look
Error Handling: update_species_details checks for empty data
Data Efficiency: Functions work with filtered data (dff) to reduce computation
Return Types: Functions return exactly what Gradio callbacks expect
2.4 Build the Gradio Interface
Create the multi-tab dashboard using Gradio:
# Get available species from data
available_species = ['All'] + sorted(df['species'].unique().tolist())
with gr.Blocks(title="Iris Dashboard", theme=gr.themes.Soft()) as app:
gr.Markdown("# 🌸 Iris Flower Analysis Dashboard")
gr.Markdown("Statistical analysis of iris flower measurements across species.")
with gr.Tabs():
# Tab 1: Iris Overview
with gr.Tab("Iris Overview"):
with gr.Row():
species_dropdown = gr.Dropdown(
choices=available_species,
value='All',
label="Select Species",
interactive=True
)
with gr.Row():
kpis = gr.Markdown()
with gr.Row():
scatter_graph = gr.Plot(label="Sepal vs Petal Length")
bar_graph = gr.Plot(label="Average Measurements by Species")
with gr.Row():
table = gr.HTML()
# Callbacks
species_dropdown.change(
fn=update_overview,
inputs=[species_dropdown],
outputs=[kpis, scatter_graph, bar_graph, table]
)
app.load(fn=update_overview,
inputs=[species_dropdown],
outputs=[kpis, scatter_graph, bar_graph, table])
# Tab 2: Species Details
with gr.Tab("Species Details"):
with gr.Row():
species_select = gr.Dropdown(
choices=available_species,
value='All',
label="Select Species"
)
with gr.Row():
species_graph = gr.Plot(label="Measurement Distribution")
# Callback
species_select.change(
fn=update_species_details,
inputs=[species_select],
outputs=[species_graph]
)
app.load(fn=update_species_details,
inputs=[species_select],
outputs=[species_graph])
# Tab 3: Advanced Analytics
with gr.Tab("Advanced Analytics"):
with gr.Row():
analytics_species = gr.Dropdown(
choices=available_species,
value='All',
label="Select Species"
)
with gr.Row():
pca_graph = gr.Plot(label="PCA Analysis")
corr_graph = gr.Plot(label="Feature Correlation")
# Callback
analytics_species.change(
fn=update_analytics,
inputs=[analytics_species],
outputs=[pca_graph, corr_graph]
)
app.load(fn=update_analytics,
inputs=[analytics_species],
outputs=[pca_graph, corr_graph])
if __name__ == "__main__":
# Use environment variables set by %set_gradio_env or start_gradio
host = os.getenv("GRADIO_HOST", "0.0.0.0")
port = int(os.getenv("GRADIO_PORT", "8001"))
root_path = os.getenv("GRADIO_ROOT_PATH", "")
app.launch(
server_name=host,
server_port=port,
root_path=root_path if root_path else None,
share=False,
quiet=True,
inline=False
)
—
3. Running the Dashboard
Jupyter Notebook Approach
In a Jupyter notebook on nanoHUB:
%load_ext nanohubgradio
%set_gradio_env
Then paste the iris dashboard code and run it. The dashboard will launch in an iframe showing interactive plots of iris flower measurements.
Standalone Script Approach
Save the complete dashboard code as dashboard_tutorial.py and run:
start_gradio dashboard_tutorial.py
The start_gradio command will:
Start the Gradio application on port 8001
Configure the weber proxy on port 8000
Automatically inject a nanoHUB header with support and terminate buttons
Display the access URL
Allow you to filter by iris species and view statistical analyses
—
4. Dashboard Features Explained
Three Analysis Tabs
The dashboard is organized into three main tabs:
Iris Overview: Shows key statistics (average sepal/petal measurements, sample counts) and scatter plots comparing flower dimensions
Species Details: Displays box plot distributions for each species
Advanced Analytics: Performs dimensionality reduction using PCA to visualize patterns in the 4-dimensional measurement space
Species Selection
species_dropdown = gr.Dropdown(
choices=['All'] + sorted(df['species'].unique().tolist()),
value='All',
label="Select Species",
interactive=True
)
Users can select “All” to view all 150 iris samples, or choose a specific species (Setosa, Versicolor, or Virginica) to view 50 samples for that species.
Interactive Callbacks
species_dropdown.change(
fn=update_overview,
inputs=[species_dropdown],
outputs=[kpis, scatter_graph, bar_graph, table]
)
When the user changes the species selection, the callback automatically updates all visualizations and KPIs to reflect the filtered data.
PCA Analysis
The Advanced Analytics tab uses scikit-learn’s PCA to reduce the 4-dimensional measurement space to 2D for visualization:
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
This reveals clustering patterns between iris species and helps identify which measurement dimensions are most important.
—
5. Customization Ideas
You can extend this dashboard with additional features:
Add more visualizations such as histogram distributions, violin plots, or parallel coordinates plots
Include statistical tests for comparing measurements between species (e.g., ANOVA, t-tests)
Add data export functionality to download filtered datasets as CSV
Create custom themes using
gr.themesto match your organization’s brandingAdd interactive sliders to filter samples by measurement ranges
Include regression analysis to predict iris species from measurements
Use more advanced ML techniques like clustering (K-means, DBSCAN) to discover patterns
—
6. Complete Example File
The complete dashboard_tutorial.py example is available in the examples/ directory of the nanohub-gradio repository. You can run it directly with:
start_gradio examples/dashboard_tutorial.py
This will launch a multi-tab dashboard showing:
Overview metrics and measurement statistics for the selected species
Detailed distributions of sepal and petal measurements
Advanced analytics using PCA to visualize 4-dimensional data in 2D space