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
- 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/
- 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)
- 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"]
}
)
])
- 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)
- 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
- 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}"
- 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")
- 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)
- 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!