Dash
by vamseeachanta
Build production-grade interactive dashboards with Plotly Dash - enterprise features, callbacks, and scalable deployment
Skill Details
Repository Files
2 files in this skill directory
name: dash version: 1.0.0 description: Build production-grade interactive dashboards with Plotly Dash - enterprise features, callbacks, and scalable deployment author: workspace-hub category: data-analysis capabilities:
- Production-ready dashboard development
- Reactive callbacks for interactivity
- Plotly visualization integration
- Multi-page application architecture
- Authentication and authorization
- Enterprise deployment options
- Custom components and extensions tools:
- dash
- plotly
- dash-bootstrap-components
- dash-ag-grid tags: [dash, dashboard, plotly, callbacks, enterprise, production, interactive, visualization] platforms: [python, web] related_skills:
- plotly
- streamlit
- polars
- pandas-data-processing
Dash Production Dashboard Skill
Build enterprise-grade interactive dashboards with Plotly Dash. Features reactive callbacks, professional layouts, and scalable deployment for production workloads.
When to Use This Skill
USE Dash when:
- Production dashboards - Building dashboards for business users
- Complex interactivity - Need fine-grained control over updates
- Enterprise requirements - Authentication, scaling, reliability needed
- Plotly ecosystem - Already using Plotly for visualizations
- Custom components - Need to extend with JavaScript/React
- Long-term projects - Dashboard will be maintained and extended
- Multi-user access - Multiple concurrent users accessing dashboards
DON'T USE Dash when:
- Quick prototypes - Use Streamlit for faster iteration
- Simple visualizations - Static reports may suffice
- No interactivity needed - Use static HTML/PDF reports
- Limited Python knowledge - Steeper learning curve than Streamlit
- Single-user tools - Jupyter notebooks may be simpler
Prerequisites
# Basic installation
pip install dash
# With common extras
pip install dash plotly pandas dash-bootstrap-components
# Full installation
pip install dash plotly pandas polars dash-bootstrap-components dash-ag-grid gunicorn
# Using uv (recommended)
uv pip install dash plotly pandas dash-bootstrap-components dash-ag-grid
Core Capabilities
1. Basic Application Structure
Minimal Dash App:
from dash import Dash, html, dcc
import plotly.express as px
import pandas as pd
# Initialize app
app = Dash(__name__)
# Sample data
df = pd.DataFrame({
"Fruit": ["Apples", "Oranges", "Bananas", "Apples", "Oranges", "Bananas"],
"Amount": [4, 1, 2, 2, 4, 5],
"City": ["SF", "SF", "SF", "NYC", "NYC", "NYC"]
})
# Create figure
fig = px.bar(df, x="Fruit", y="Amount", color="City", barmode="group")
# Layout
app.layout = html.Div([
html.H1("Hello Dash"),
html.P("This is a simple Dash application."),
dcc.Graph(id="example-graph", figure=fig)
])
# Run server
if __name__ == "__main__":
app.run(debug=True)
Run the app:
python app.py
# Visit http://127.0.0.1:8050/
2. Callbacks and Interactivity
Basic Callback:
from dash import Dash, html, dcc, callback, Output, Input
import plotly.express as px
import pandas as pd
app = Dash(__name__)
# Sample data
df = pd.DataFrame({
"date": pd.date_range("2025-01-01", periods=100),
"category": ["A", "B", "C", "D"] * 25,
"value": range(100)
})
# Layout
app.layout = html.Div([
html.H1("Interactive Dashboard"),
html.Label("Select Category:"),
dcc.Dropdown(
id="category-dropdown",
options=[{"label": c, "value": c} for c in df["category"].unique()],
value="A",
clearable=False
),
dcc.Graph(id="line-chart")
])
# Callback
@callback(
Output("line-chart", "figure"),
Input("category-dropdown", "value")
)
def update_chart(selected_category):
filtered_df = df[df["category"] == selected_category]
fig = px.line(
filtered_df,
x="date",
y="value",
title=f"Values for Category {selected_category}"
)
return fig
if __name__ == "__main__":
app.run(debug=True)
Multiple Inputs and Outputs:
from dash import Dash, html, dcc, callback, Output, Input
import plotly.express as px
import pandas as pd
app = Dash(__name__)
# Sample data
df = pd.DataFrame({
"date": pd.date_range("2025-01-01", periods=365),
"category": ["A", "B", "C"] * 122 + ["A"],
"region": ["North", "South", "East", "West"] * 91 + ["North"],
"value": [i + (i % 30) * 10 for i in range(365)]
})
app.layout = html.Div([
html.H1("Multi-Input Dashboard"),
html.Div([
html.Div([
html.Label("Category"),
dcc.Dropdown(
id="category-filter",
options=[{"label": c, "value": c} for c in df["category"].unique()],
value=["A", "B", "C"],
multi=True
)
], style={"width": "45%", "display": "inline-block"}),
html.Div([
html.Label("Region"),
dcc.Dropdown(
id="region-filter",
options=[{"label": r, "value": r} for r in df["region"].unique()],
value=["North", "South", "East", "West"],
multi=True
)
], style={"width": "45%", "display": "inline-block", "marginLeft": "5%"})
]),
html.Div([
html.Div([
dcc.Graph(id="trend-chart")
], style={"width": "60%", "display": "inline-block"}),
html.Div([
dcc.Graph(id="pie-chart")
], style={"width": "38%", "display": "inline-block", "marginLeft": "2%"})
]),
html.Div(id="summary-stats")
])
@callback(
[Output("trend-chart", "figure"),
Output("pie-chart", "figure"),
Output("summary-stats", "children")],
[Input("category-filter", "value"),
Input("region-filter", "value")]
)
def update_all(categories, regions):
# Filter data
filtered = df[
(df["category"].isin(categories)) &
(df["region"].isin(regions))
]
# Trend chart
trend = filtered.groupby("date")["value"].sum().reset_index()
trend_fig = px.line(trend, x="date", y="value", title="Value Trend")
# Pie chart
by_category = filtered.groupby("category")["value"].sum().reset_index()
pie_fig = px.pie(by_category, values="value", names="category", title="By Category")
# Summary stats
stats = html.Div([
html.H4("Summary Statistics"),
html.P(f"Total records: {len(filtered):,}"),
html.P(f"Total value: {filtered['value'].sum():,}"),
html.P(f"Average value: {filtered['value'].mean():.2f}")
])
return trend_fig, pie_fig, stats
if __name__ == "__main__":
app.run(debug=True)
Chained Callbacks:
from dash import Dash, html, dcc, callback, Output, Input
import pandas as pd
app = Dash(__name__)
# Hierarchical data
data = {
"USA": {"California": ["San Francisco", "Los Angeles"], "Texas": ["Houston", "Dallas"]},
"Canada": {"Ontario": ["Toronto", "Ottawa"], "Quebec": ["Montreal", "Quebec City"]}
}
app.layout = html.Div([
html.H1("Chained Dropdowns"),
html.Label("Country"),
dcc.Dropdown(id="country-dropdown"),
html.Label("State/Province"),
dcc.Dropdown(id="state-dropdown"),
html.Label("City"),
dcc.Dropdown(id="city-dropdown"),
html.Div(id="selection-output")
])
# Populate country dropdown
@callback(
Output("country-dropdown", "options"),
Input("country-dropdown", "id") # Dummy input to trigger on load
)
def set_countries(_):
return [{"label": c, "value": c} for c in data.keys()]
# Update state options based on country
@callback(
Output("state-dropdown", "options"),
Output("state-dropdown", "value"),
Input("country-dropdown", "value")
)
def set_states(country):
if country is None:
return [], None
states = data.get(country, {}).keys()
return [{"label": s, "value": s} for s in states], None
# Update city options based on state
@callback(
Output("city-dropdown", "options"),
Output("city-dropdown", "value"),
Input("country-dropdown", "value"),
Input("state-dropdown", "value")
)
def set_cities(country, state):
if country is None or state is None:
return [], None
cities = data.get(country, {}).get(state, [])
return [{"label": c, "value": c} for c in cities], None
# Display selection
@callback(
Output("selection-output", "children"),
Input("country-dropdown", "value"),
Input("state-dropdown", "value"),
Input("city-dropdown", "value")
)
def display_selection(country, state, city):
return f"Selected: {country or '-'} > {state or '-'} > {city or '-'}"
if __name__ == "__main__":
app.run(debug=True)
3. Layout Components
HTML Components:
from dash import html
# Text elements
layout = html.Div([
html.H1("Main Title"),
html.H2("Subtitle"),
html.H3("Section Header"),
html.P("Paragraph text with ", html.Strong("bold"), " and ", html.Em("italic")),
html.Hr(), # Horizontal rule
html.Br(), # Line break
# Lists
html.Ul([
html.Li("Item 1"),
html.Li("Item 2"),
html.Li("Item 3")
]),
# Links
html.A("Click here", href="https://example.com", target="_blank"),
# Images
html.Img(src="/assets/logo.png", style={"width": "200px"}),
# Tables
html.Table([
html.Thead([
html.Tr([html.Th("Name"), html.Th("Value")])
]),
html.Tbody([
html.Tr([html.Td("Item 1"), html.Td("100")]),
html.Tr([html.Td("Item 2"), html.Td("200")])
])
])
])
Core Components (dcc):
from dash import dcc
# Input components
components = html.Div([
# Dropdown
dcc.Dropdown(
id="dropdown",
options=[
{"label": "Option A", "value": "a"},
{"label": "Option B", "value": "b"},
{"label": "Option C", "value": "c", "disabled": True}
],
value="a",
multi=False,
clearable=True,
searchable=True,
placeholder="Select..."
),
# Multi-select dropdown
dcc.Dropdown(
id="multi-dropdown",
options=[{"label": f"Option {i}", "value": i} for i in range(10)],
value=[1, 2, 3],
multi=True
),
# Slider
dcc.Slider(
id="slider",
min=0,
max=100,
step=5,
value=50,
marks={0: "0", 25: "25", 50: "50", 75: "75", 100: "100"}
),
# Range slider
dcc.RangeSlider(
id="range-slider",
min=0,
max=100,
step=1,
value=[20, 80],
marks={i: str(i) for i in range(0, 101, 20)}
),
# Input
dcc.Input(
id="text-input",
type="text",
placeholder="Enter text...",
debounce=True # Wait for typing to stop
),
# Textarea
dcc.Textarea(
id="textarea",
placeholder="Enter longer text...",
style={"width": "100%", "height": "100px"}
),
# Checklist
dcc.Checklist(
id="checklist",
options=[
{"label": "Option 1", "value": "1"},
{"label": "Option 2", "value": "2"},
{"label": "Option 3", "value": "3"}
],
value=["1"],
inline=True
),
# Radio items
dcc.RadioItems(
id="radio",
options=[
{"label": "Small", "value": "s"},
{"label": "Medium", "value": "m"},
{"label": "Large", "value": "l"}
],
value="m",
inline=True
),
# Date picker
dcc.DatePickerSingle(
id="date-picker",
date="2025-01-01",
display_format="YYYY-MM-DD"
),
# Date range picker
dcc.DatePickerRange(
id="date-range",
start_date="2025-01-01",
end_date="2025-12-31",
display_format="YYYY-MM-DD"
),
# Upload
dcc.Upload(
id="upload",
children=html.Div(["Drag and Drop or ", html.A("Select Files")]),
style={
"width": "100%",
"height": "60px",
"lineHeight": "60px",
"borderWidth": "1px",
"borderStyle": "dashed",
"borderRadius": "5px",
"textAlign": "center"
}
),
# Tabs
dcc.Tabs(id="tabs", value="tab-1", children=[
dcc.Tab(label="Tab 1", value="tab-1"),
dcc.Tab(label="Tab 2", value="tab-2")
]),
# Loading indicator
dcc.Loading(
id="loading",
type="default", # default, graph, cube, circle, dot
children=html.Div(id="loading-output")
),
# Interval (for periodic updates)
dcc.Interval(
id="interval-component",
interval=1000, # milliseconds
n_intervals=0
),
# Store (client-side data storage)
dcc.Store(id="data-store", storage_type="session"), # memory, session, local
# Graph
dcc.Graph(
id="graph",
config={
"displayModeBar": True,
"displaylogo": False,
"modeBarButtonsToRemove": ["lasso2d", "select2d"]
}
)
])
4. Bootstrap Components
Using Dash Bootstrap Components:
from dash import Dash, html, dcc, callback, Output, Input
import dash_bootstrap_components as dbc
import plotly.express as px
import pandas as pd
# Initialize with Bootstrap theme
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
# Sample data
df = pd.DataFrame({
"date": pd.date_range("2025-01-01", periods=100),
"sales": [100 + i * 2 + (i % 7) * 10 for i in range(100)],
"orders": [50 + i + (i % 5) * 5 for i in range(100)]
})
# Layout with Bootstrap components
app.layout = dbc.Container([
# Header
dbc.Row([
dbc.Col([
html.H1("Sales Dashboard", className="text-primary"),
html.P("Interactive analytics powered by Dash", className="lead")
])
], className="mb-4"),
# Metrics row
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardBody([
html.H4("Total Sales", className="card-title"),
html.H2(f"${df['sales'].sum():,}", className="text-success")
])
])
], md=4),
dbc.Col([
dbc.Card([
dbc.CardBody([
html.H4("Total Orders", className="card-title"),
html.H2(f"{df['orders'].sum():,}", className="text-info")
])
])
], md=4),
dbc.Col([
dbc.Card([
dbc.CardBody([
html.H4("Avg Order Value", className="card-title"),
html.H2(f"${df['sales'].sum() / df['orders'].sum():.2f}", className="text-warning")
])
])
], md=4)
], className="mb-4"),
# Filters
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardHeader("Filters"),
dbc.CardBody([
dbc.Label("Date Range"),
dcc.DatePickerRange(
id="date-range",
start_date=df["date"].min(),
end_date=df["date"].max(),
className="mb-3"
),
dbc.Label("Metric"),
dcc.Dropdown(
id="metric-dropdown",
options=[
{"label": "Sales", "value": "sales"},
{"label": "Orders", "value": "orders"}
],
value="sales"
)
])
])
], md=3),
dbc.Col([
dcc.Graph(id="main-chart")
], md=9)
]),
# Tabs
dbc.Row([
dbc.Col([
dbc.Tabs([
dbc.Tab(label="Daily Data", tab_id="daily"),
dbc.Tab(label="Summary", tab_id="summary")
], id="tabs", active_tab="daily"),
html.Div(id="tab-content", className="mt-3")
])
], className="mt-4")
], fluid=True)
@callback(
Output("main-chart", "figure"),
[Input("date-range", "start_date"),
Input("date-range", "end_date"),
Input("metric-dropdown", "value")]
)
def update_chart(start_date, end_date, metric):
filtered = df[
(df["date"] >= start_date) &
(df["date"] <= end_date)
]
fig = px.line(
filtered,
x="date",
y=metric,
title=f"{metric.title()} Over Time"
)
fig.update_layout(template="plotly_white")
return fig
@callback(
Output("tab-content", "children"),
Input("tabs", "active_tab")
)
def render_tab(tab):
if tab == "daily":
return dbc.Table.from_dataframe(
df.tail(10),
striped=True,
bordered=True,
hover=True
)
elif tab == "summary":
return html.Div([
html.P(f"Total Records: {len(df)}"),
html.P(f"Date Range: {df['date'].min()} to {df['date'].max()}"),
html.P(f"Sales Range: ${df['sales'].min()} - ${df['sales'].max()}")
])
if __name__ == "__main__":
app.run(debug=True)
5. Multi-Page Applications
Project Structure:
my_dash_app/
├── app.py # Main entry point
├── pages/
│ ├── __init__.py
│ ├── home.py
│ ├── analytics.py
│ └── settings.py
├── components/
│ ├── __init__.py
│ ├── navbar.py
│ └── footer.py
├── utils/
│ ├── __init__.py
│ └── data.py
└── assets/
├── style.css
└── logo.png
Main App (app.py):
from dash import Dash, html, dcc, page_container
import dash_bootstrap_components as dbc
app = Dash(
__name__,
use_pages=True,
external_stylesheets=[dbc.themes.BOOTSTRAP]
)
# Navbar
navbar = dbc.NavbarSimple(
children=[
dbc.NavItem(dbc.NavLink("Home", href="/")),
dbc.NavItem(dbc.NavLink("Analytics", href="/analytics")),
dbc.NavItem(dbc.NavLink("Settings", href="/settings")),
],
brand="My Dashboard",
brand_href="/",
color="primary",
dark=True,
)
# Layout with navigation and page container
app.layout = html.Div([
navbar,
dbc.Container([
page_container
], fluid=True, className="mt-4")
])
if __name__ == "__main__":
app.run(debug=True)
Home Page (pages/home.py):
from dash import html, register_page
import dash_bootstrap_components as dbc
register_page(__name__, path="/", name="Home")
layout = dbc.Container([
dbc.Row([
dbc.Col([
html.H1("Welcome to the Dashboard"),
html.P("Select a page from the navigation bar to get started."),
dbc.Card([
dbc.CardBody([
html.H4("Quick Links"),
dbc.ListGroup([
dbc.ListGroupItem("Analytics", href="/analytics"),
dbc.ListGroupItem("Settings", href="/settings")
])
])
])
])
])
])
Analytics Page (pages/analytics.py):
from dash import html, dcc, callback, Output, Input, register_page
import dash_bootstrap_components as dbc
import plotly.express as px
import pandas as pd
register_page(__name__, path="/analytics", name="Analytics")
# Generate sample data
df = pd.DataFrame({
"date": pd.date_range("2025-01-01", periods=365),
"value": [100 + i + (i % 30) * 5 for i in range(365)]
})
layout = dbc.Container([
html.H1("Analytics"),
dbc.Row([
dbc.Col([
dbc.Label("Chart Type"),
dcc.Dropdown(
id="chart-type",
options=[
{"label": "Line", "value": "line"},
{"label": "Bar", "value": "bar"},
{"label": "Area", "value": "area"}
],
value="line"
)
], md=4)
], className="mb-4"),
dcc.Graph(id="analytics-chart")
])
@callback(
Output("analytics-chart", "figure"),
Input("chart-type", "value")
)
def update_chart(chart_type):
if chart_type == "line":
fig = px.line(df, x="date", y="value")
elif chart_type == "bar":
monthly = df.resample("M", on="date")["value"].sum().reset_index()
fig = px.bar(monthly, x="date", y="value")
else:
fig = px.area(df, x="date", y="value")
return fig
6. Authentication
Basic Authentication:
from dash import Dash, html, dcc
import dash_auth
app = Dash(__name__)
# Basic authentication
VALID_USERNAME_PASSWORD_PAIRS = {
"admin": "admin123",
"user": "user123"
}
auth = dash_auth.BasicAuth(
app,
VALID_USERNAME_PASSWORD_PAIRS
)
app.layout = html.Div([
html.H1("Protected Dashboard"),
html.P("You are authenticated!")
])
if __name__ == "__main__":
app.run(debug=True)
Custom Login (with session):
from dash import Dash, html, dcc, callback, Output, Input, State
import dash_bootstrap_components as dbc
from flask import session
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.server.secret_key = "your-secret-key-here"
# Login form
login_form = dbc.Card([
dbc.CardBody([
html.H4("Login"),
dbc.Input(id="username", placeholder="Username", className="mb-2"),
dbc.Input(id="password", type="password", placeholder="Password", className="mb-2"),
dbc.Button("Login", id="login-btn", color="primary"),
html.Div(id="login-message")
])
], style={"maxWidth": "400px", "margin": "100px auto"})
# Main content
main_content = html.Div([
html.H1("Dashboard"),
html.P("Welcome! You are logged in."),
dbc.Button("Logout", id="logout-btn", color="secondary")
])
app.layout = html.Div([
dcc.Location(id="url"),
html.Div(id="page-content")
])
@callback(
Output("page-content", "children"),
Input("url", "pathname")
)
def display_page(pathname):
if session.get("authenticated"):
return main_content
return login_form
@callback(
[Output("login-message", "children"),
Output("url", "pathname")],
Input("login-btn", "n_clicks"),
[State("username", "value"),
State("password", "value")],
prevent_initial_call=True
)
def login(n_clicks, username, password):
# Simple validation (use proper auth in production)
if username == "admin" and password == "admin123":
session["authenticated"] = True
return "", "/"
return dbc.Alert("Invalid credentials", color="danger"), "/"
@callback(
Output("url", "pathname", allow_duplicate=True),
Input("logout-btn", "n_clicks"),
prevent_initial_call=True
)
def logout(n_clicks):
session.clear()
return "/"
if __name__ == "__main__":
app.run(debug=True)
Complete Examples
Example 1: Sales Analytics Dashboard
from dash import Dash, html, dcc, callback, Output, Input
import dash_bootstrap_components as dbc
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
# Initialize app
app = Dash(__name__, external_stylesheets=[dbc.themes.FLATLY])
# Generate sample data
np.random.seed(42)
dates = pd.date_range("2024-01-01", "2025-12-31", freq="D")
n_days = len(dates)
df = pd.DataFrame({
"date": dates,
"revenue": np.cumsum(np.random.randn(n_days) * 100 + 500),
"orders": np.random.poisson(100, n_days),
"customers": np.random.poisson(80, n_days),
"region": np.random.choice(["North", "South", "East", "West"], n_days),
"category": np.random.choice(["Electronics", "Clothing", "Food", "Home"], n_days)
})
# Calculate previous period metrics
current_revenue = df[df["date"] >= "2025-01-01"]["revenue"].sum()
prev_revenue = df[df["date"] < "2025-01-01"]["revenue"].sum()
revenue_change = ((current_revenue - prev_revenue) / prev_revenue * 100)
# Layout
app.layout = dbc.Container([
# Header
dbc.Row([
dbc.Col([
html.H1("Sales Analytics Dashboard", className="text-primary mb-0"),
html.P(f"Last updated: {datetime.now().strftime('%Y-%m-%d %H:%M')}", className="text-muted")
], md=8),
dbc.Col([
dbc.ButtonGroup([
dbc.Button("Export", outline=True, color="primary"),
dbc.Button("Refresh", outline=True, color="secondary")
])
], md=4, className="text-end")
], className="mb-4 mt-3"),
# Date filter
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardBody([
dbc.Row([
dbc.Col([
dbc.Label("Date Range"),
dcc.DatePickerRange(
id="date-filter",
start_date="2025-01-01",
end_date="2025-12-31",
display_format="YYYY-MM-DD"
)
], md=4),
dbc.Col([
dbc.Label("Region"),
dcc.Dropdown(
id="region-filter",
options=[{"label": r, "value": r} for r in df["region"].unique()],
value=df["region"].unique().tolist(),
multi=True
)
], md=4),
dbc.Col([
dbc.Label("Category"),
dcc.Dropdown(
id="category-filter",
options=[{"label": c, "value": c} for c in df["category"].unique()],
value=df["category"].unique().tolist(),
multi=True
)
], md=4)
])
])
])
])
], className="mb-4"),
# KPI Cards
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardBody([
html.H6("Total Revenue", className="text-muted"),
html.H3(id="kpi-revenue", className="text-success"),
html.Small(id="kpi-revenue-change", className="text-muted")
])
], color="light")
], md=3),
dbc.Col([
dbc.Card([
dbc.CardBody([
html.H6("Total Orders", className="text-muted"),
html.H3(id="kpi-orders", className="text-info"),
html.Small(id="kpi-orders-change", className="text-muted")
])
], color="light")
], md=3),
dbc.Col([
dbc.Card([
dbc.CardBody([
html.H6("Unique Customers", className="text-muted"),
html.H3(id="kpi-customers", className="text-warning"),
html.Small(id="kpi-customers-change", className="text-muted")
])
], color="light")
], md=3),
dbc.Col([
dbc.Card([
dbc.CardBody([
html.H6("Avg Order Value", className="text-muted"),
html.H3(id="kpi-aov", className="text-primary"),
html.Small(id="kpi-aov-change", className="text-muted")
])
], color="light")
], md=3)
], className="mb-4"),
# Charts Row 1
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardHeader("Revenue Trend"),
dbc.CardBody([
dcc.Graph(id="revenue-trend")
])
])
], md=8),
dbc.Col([
dbc.Card([
dbc.CardHeader("Revenue by Category"),
dbc.CardBody([
dcc.Graph(id="category-pie")
])
])
], md=4)
], className="mb-4"),
# Charts Row 2
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardHeader("Regional Performance"),
dbc.CardBody([
dcc.Graph(id="regional-bar")
])
])
], md=6),
dbc.Col([
dbc.Card([
dbc.CardHeader("Orders vs Customers"),
dbc.CardBody([
dcc.Graph(id="scatter-chart")
])
])
], md=6)
], className="mb-4"),
# Data Table
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardHeader("Detailed Data"),
dbc.CardBody([
html.Div(id="data-table")
])
])
])
])
], fluid=True)
# Callbacks
@callback(
[Output("kpi-revenue", "children"),
Output("kpi-orders", "children"),
Output("kpi-customers", "children"),
Output("kpi-aov", "children"),
Output("revenue-trend", "figure"),
Output("category-pie", "figure"),
Output("regional-bar", "figure"),
Output("scatter-chart", "figure"),
Output("data-table", "children")],
[Input("date-filter", "start_date"),
Input("date-filter", "end_date"),
Input("region-filter", "value"),
Input("category-filter", "value")]
)
def update_dashboard(start_date, end_date, regions, categories):
# Filter data
filtered = df[
(df["date"] >= start_date) &
(df["date"] <= end_date) &
(df["region"].isin(regions)) &
(df["category"].isin(categories))
]
# KPIs
revenue = f"${filtered['revenue'].sum():,.0f}"
orders = f"{filtered['orders'].sum():,}"
customers = f"{filtered['customers'].sum():,}"
aov = f"${filtered['revenue'].sum() / filtered['orders'].sum():.2f}" if filtered['orders'].sum() > 0 else "$0"
# Revenue trend
daily_revenue = filtered.groupby("date")["revenue"].sum().reset_index()
trend_fig = px.line(
daily_revenue,
x="date",
y="revenue",
title=None
)
trend_fig.update_layout(
margin=dict(l=0, r=0, t=0, b=0),
hovermode="x unified"
)
# Category pie
by_category = filtered.groupby("category")["revenue"].sum().reset_index()
pie_fig = px.pie(
by_category,
values="revenue",
names="category",
title=None
)
pie_fig.update_layout(margin=dict(l=0, r=0, t=0, b=0))
# Regional bar
by_region = filtered.groupby("region").agg({
"revenue": "sum",
"orders": "sum"
}).reset_index()
bar_fig = px.bar(
by_region,
x="region",
y="revenue",
color="region",
title=None
)
bar_fig.update_layout(
margin=dict(l=0, r=0, t=0, b=0),
showlegend=False
)
# Scatter
scatter_fig = px.scatter(
filtered.groupby("date").agg({"orders": "sum", "customers": "sum"}).reset_index(),
x="orders",
y="customers",
title=None,
trendline="ols"
)
scatter_fig.update_layout(margin=dict(l=0, r=0, t=0, b=0))
# Table
table = dbc.Table.from_dataframe(
filtered.groupby(["region", "category"]).agg({
"revenue": "sum",
"orders": "sum",
"customers": "sum"
}).reset_index().round(2),
striped=True,
bordered=True,
hover=True,
responsive=True
)
return revenue, orders, customers, aov, trend_fig, pie_fig, bar_fig, scatter_fig, table
if __name__ == "__main__":
app.run(debug=True)
Example 2: Real-Time Monitoring Dashboard
from dash import Dash, html, dcc, callback, Output, Input
import dash_bootstrap_components as dbc
import plotly.graph_objects as go
from collections import deque
import random
from datetime import datetime
app = Dash(__name__, external_stylesheets=[dbc.themes.CYBORG])
# Initialize data stores
MAX_POINTS = 50
time_data = deque(maxlen=MAX_POINTS)
cpu_data = deque(maxlen=MAX_POINTS)
memory_data = deque(maxlen=MAX_POINTS)
network_data = deque(maxlen=MAX_POINTS)
# Layout
app.layout = dbc.Container([
html.H1("Real-Time System Monitor", className="text-center my-4"),
# Status indicators
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardBody([
html.H6("CPU Usage"),
html.H2(id="cpu-value", className="text-info"),
dbc.Progress(id="cpu-progress", value=0, max=100)
])
])
], md=4),
dbc.Col([
dbc.Card([
dbc.CardBody([
html.H6("Memory Usage"),
html.H2(id="memory-value", className="text-warning"),
dbc.Progress(id="memory-progress", value=0, max=100)
])
])
], md=4),
dbc.Col([
dbc.Card([
dbc.CardBody([
html.H6("Network I/O"),
html.H2(id="network-value", className="text-success"),
dbc.Progress(id="network-progress", value=0, max=100)
])
])
], md=4)
], className="mb-4"),
# Charts
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardHeader("System Metrics (Last 50 Updates)"),
dbc.CardBody([
dcc.Graph(id="live-graph", animate=True)
])
])
])
]),
# Interval component for updates
dcc.Interval(
id="interval-component",
interval=1000, # 1 second
n_intervals=0
)
], fluid=True)
@callback(
[Output("cpu-value", "children"),
Output("memory-value", "children"),
Output("network-value", "children"),
Output("cpu-progress", "value"),
Output("memory-progress", "value"),
Output("network-progress", "value"),
Output("live-graph", "figure")],
Input("interval-component", "n_intervals")
)
def update_metrics(n):
# Simulate metrics
cpu = random.uniform(20, 80)
memory = random.uniform(40, 90)
network = random.uniform(10, 60)
# Update data stores
time_data.append(datetime.now())
cpu_data.append(cpu)
memory_data.append(memory)
network_data.append(network)
# Create figure
fig = go.Figure()
fig.add_trace(go.Scatter(
x=list(time_data),
y=list(cpu_data),
name="CPU",
mode="lines",
line=dict(color="#17a2b8")
))
fig.add_trace(go.Scatter(
x=list(time_data),
y=list(memory_data),
name="Memory",
mode="lines",
line=dict(color="#ffc107")
))
fig.add_trace(go.Scatter(
x=list(time_data),
y=list(network_data),
name="Network",
mode="lines",
line=dict(color="#28a745")
))
fig.update_layout(
template="plotly_dark",
paper_bgcolor="rgba(0,0,0,0)",
plot_bgcolor="rgba(0,0,0,0)",
yaxis=dict(range=[0, 100], title="Usage %"),
xaxis=dict(title="Time"),
legend=dict(orientation="h", yanchor="bottom", y=1.02),
margin=dict(l=50, r=20, t=30, b=50),
uirevision="constant" # Preserve zoom/pan on update
)
return (
f"{cpu:.1f}%",
f"{memory:.1f}%",
f"{network:.1f}%",
cpu,
memory,
network,
fig
)
if __name__ == "__main__":
app.run(debug=True)
Example 3: Data Table with AG Grid
from dash import Dash, html, callback, Output, Input
import dash_ag_grid as dag
import dash_bootstrap_components as dbc
import pandas as pd
import numpy as np
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
# Generate sample data
np.random.seed(42)
df = pd.DataFrame({
"ID": range(1, 1001),
"Name": [f"Product {i}" for i in range(1, 1001)],
"Category": np.random.choice(["Electronics", "Clothing", "Food", "Home"], 1000),
"Price": np.random.uniform(10, 500, 1000).round(2),
"Stock": np.random.randint(0, 100, 1000),
"Rating": np.random.uniform(1, 5, 1000).round(1),
"Last Updated": pd.date_range("2025-01-01", periods=1000, freq="H")
})
# Column definitions
column_defs = [
{"field": "ID", "filter": "agNumberColumnFilter", "width": 80},
{"field": "Name", "filter": "agTextColumnFilter"},
{
"field": "Category",
"filter": "agSetColumnFilter",
"cellStyle": {"fontWeight": "bold"}
},
{
"field": "Price",
"filter": "agNumberColumnFilter",
"valueFormatter": {"function": "'$' + params.value.toFixed(2)"},
"cellStyle": {
"function": "params.value > 300 ? {'color': 'red'} : {'color': 'green'}"
}
},
{
"field": "Stock",
"filter": "agNumberColumnFilter",
"cellStyle": {
"function": "params.value < 10 ? {'backgroundColor': '#ffcccc'} : {}"
}
},
{
"field": "Rating",
"filter": "agNumberColumnFilter",
"cellRenderer": "agSparklineCellRenderer",
"cellRendererParams": {
"sparklineOptions": {
"type": "bar",
"fill": "#5470c6"
}
}
},
{
"field": "Last Updated",
"filter": "agDateColumnFilter",
"valueFormatter": {"function": "new Date(params.value).toLocaleDateString()"}
}
]
# Layout
app.layout = dbc.Container([
html.H1("Product Inventory", className="my-4"),
dbc.Row([
dbc.Col([
dbc.Input(
id="search-input",
placeholder="Quick search...",
className="mb-3"
)
], md=4),
dbc.Col([
dbc.Button("Export CSV", id="export-btn", color="primary")
], md=2)
]),
dag.AgGrid(
id="inventory-grid",
columnDefs=column_defs,
rowData=df.to_dict("records"),
defaultColDef={
"sortable": True,
"filter": True,
"resizable": True,
"floatingFilter": True
},
dashGridOptions={
"pagination": True,
"paginationPageSize": 20,
"rowSelection": "multiple",
"animateRows": True
},
style={"height": "600px"}
),
html.Div(id="selection-output", className="mt-3")
], fluid=True)
@callback(
Output("inventory-grid", "dashGridOptions"),
Input("search-input", "value")
)
def update_search(search_value):
return {
"pagination": True,
"paginationPageSize": 20,
"rowSelection": "multiple",
"animateRows": True,
"quickFilterText": search_value
}
@callback(
Output("selection-output", "children"),
Input("inventory-grid", "selectedRows")
)
def display_selection(selected):
if selected:
return dbc.Alert(
f"Selected {len(selected)} items. Total value: ${sum(r['Price'] for r in selected):,.2f}",
color="info"
)
return ""
if __name__ == "__main__":
app.run(debug=True)
Deployment Patterns
Gunicorn Production Server
# wsgi.py
from app import app
server = app.server
if __name__ == "__main__":
server.run()
# Run with Gunicorn
gunicorn wsgi:server -b 0.0.0.0:8050 -w 4
Docker Deployment
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8050
CMD ["gunicorn", "wsgi:server", "-b", "0.0.0.0:8050", "-w", "4"]
# docker-compose.yml
version: "3.8"
services:
dash:
build: .
ports:
- "8050:8050"
environment:
- DASH_DEBUG=false
restart: unless-stopped
Cloud Deployment (Heroku)
# Procfile
web: gunicorn wsgi:server
# requirements.txt
dash>=2.14.0
dash-bootstrap-components>=1.5.0
plotly>=5.18.0
pandas>=2.0.0
gunicorn>=21.0.0
Best Practices
1. Optimize Callback Performance
# Use prevent_initial_call when appropriate
@callback(
Output("output", "children"),
Input("button", "n_clicks"),
prevent_initial_call=True
)
def handle_click(n_clicks):
return f"Clicked {n_clicks} times"
# Use State for non-triggering inputs
@callback(
Output("output", "children"),
Input("submit-btn", "n_clicks"),
State("input-field", "value") # Doesn't trigger callback
)
def submit_form(n_clicks, value):
return f"Submitted: {value}"
2. Efficient Data Loading
# Cache expensive computations
from flask_caching import Cache
cache = Cache(app.server, config={"CACHE_TYPE": "simple"})
@cache.memoize(timeout=300)
def load_data():
return pd.read_parquet("large_file.parquet")
3. Modular Callbacks
# Separate callbacks into modules
# callbacks/analytics.py
from dash import callback, Output, Input
def register_callbacks(app):
@callback(
Output("chart", "figure"),
Input("dropdown", "value")
)
def update_chart(value):
return create_figure(value)
4. Error Handling
from dash import callback, Output, Input
from dash.exceptions import PreventUpdate
@callback(
Output("output", "children"),
Input("input", "value")
)
def safe_callback(value):
if value is None:
raise PreventUpdate
try:
result = process(value)
return result
except Exception as e:
return html.Div(f"Error: {str(e)}", className="text-danger")
Troubleshooting
Common Issues
Issue: Callback not firing
# Check component IDs match exactly
# Verify Input/Output/State decorators
# Check for circular dependencies
Issue: Slow initial load
# Use loading states
dcc.Loading(
children=[dcc.Graph(id="graph")],
type="circle"
)
Issue: Memory leaks
# Clear caches periodically
# Use background callbacks for long operations
# Limit data in client-side stores
Issue: Multiple callback outputs
# Use allow_duplicate=True for same output
@callback(
Output("output", "children", allow_duplicate=True),
Input("button2", "n_clicks"),
prevent_initial_call=True
)
Version History
- 1.0.0 (2026-01-17): Initial release
- Core application structure
- Callbacks and interactivity
- Layout components (HTML, DCC, Bootstrap)
- Multi-page applications
- Authentication patterns
- Complete dashboard examples
- Real-time monitoring example
- AG Grid integration
- Deployment patterns
- Best practices and troubleshooting
Resources
- Official Docs: https://dash.plotly.com/
- Components: https://dash.plotly.com/dash-core-components
- Bootstrap Components: https://dash-bootstrap-components.opensource.faculty.ai/
- AG Grid: https://dash.plotly.com/dash-ag-grid
- Enterprise: https://plotly.com/dash/
- GitHub: https://github.com/plotly/dash
Build enterprise-grade interactive dashboards with Python and Plotly!
Related Skills
Attack Tree Construction
Build comprehensive attack trees to visualize threat paths. Use when mapping attack scenarios, identifying defense gaps, or communicating security risks to stakeholders.
Grafana Dashboards
Create and manage production Grafana dashboards for real-time visualization of system and application metrics. Use when building monitoring dashboards, visualizing metrics, or creating operational observability interfaces.
Matplotlib
Foundational plotting library. Create line plots, scatter, bar, histograms, heatmaps, 3D, subplots, export PNG/PDF/SVG, for scientific visualization and publication figures.
Scientific Visualization
Create publication figures with matplotlib/seaborn/plotly. Multi-panel layouts, error bars, significance markers, colorblind-safe, export PDF/EPS/TIFF, for journal-ready scientific plots.
Seaborn
Statistical visualization. Scatter, box, violin, heatmaps, pair plots, regression, correlation matrices, KDE, faceted plots, for exploratory analysis and publication figures.
Shap
Model interpretability and explainability using SHAP (SHapley Additive exPlanations). Use this skill when explaining machine learning model predictions, computing feature importance, generating SHAP plots (waterfall, beeswarm, bar, scatter, force, heatmap), debugging models, analyzing model bias or fairness, comparing models, or implementing explainable AI. Works with tree-based models (XGBoost, LightGBM, Random Forest), deep learning (TensorFlow, PyTorch), linear models, and any black-box model
Pydeseq2
Differential gene expression analysis (Python DESeq2). Identify DE genes from bulk RNA-seq counts, Wald tests, FDR correction, volcano/MA plots, for RNA-seq analysis.
Query Writing
For writing and executing SQL queries - from simple single-table queries to complex multi-table JOINs and aggregations
Pydeseq2
Differential gene expression analysis (Python DESeq2). Identify DE genes from bulk RNA-seq counts, Wald tests, FDR correction, volcano/MA plots, for RNA-seq analysis.
Scientific Visualization
Meta-skill for publication-ready figures. Use when creating journal submission figures requiring multi-panel layouts, significance annotations, error bars, colorblind-safe palettes, and specific journal formatting (Nature, Science, Cell). Orchestrates matplotlib/seaborn/plotly with publication styles. For quick exploration use seaborn or plotly directly.
