NYC

dash

SKILL.md

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


Build enterprise-grade interactive dashboards with Python and Plotly!

Weekly Installs
16
First Seen
Jan 24, 2026
Installed on
claude-code12
codex12
gemini-cli12
antigravity11
cursor11
opencode11