Exercises: Dash Dashboards
Install: pip install dash dash-bootstrap-components plotly pandas. Run apps with python app.py and open http://127.0.0.1:8050.
Part A: Conceptual (6 problems)
A.1 ★☆☆ | Recall
Name the two main parts of a Dash application.
Guidance
**Layout** (a tree of html and dcc components) and **callbacks** (reactive functions decorated with `@app.callback`).A.2 ★☆☆ | Recall
What is the difference between Input and State in a callback?
Guidance
`Input`: a component property whose change triggers the callback. `State`: a component property whose value is read but does not trigger the callback. State is used for form patterns where you want to read a value only when a button is clicked.A.3 ★★☆ | Understand
Describe the chapter's threshold concept ("callbacks are reactive declarations").
Guidance
In Dash, you do not write "when the slider moves, do X" (imperative). You write "output Y depends on inputs A, B, C" (declarative). The framework handles when and how to call the function. You are declaring dependencies, not describing actions.A.4 ★★☆ | Understand
How does Dash's clickData enable cross-filtering?
Guidance
When a user clicks a point in a `dcc.Graph`, Plotly updates the `clickData` property with information about the clicked point. A callback with `Input("source-chart", "clickData")` can read this data and update another chart based on it. This is how brushing-and-linking between charts works in Dash.A.5 ★★★ | Analyze
When should you prefer Dash over Streamlit?
Guidance
When you need: cross-filtering between multiple charts, auto-refresh for live data, complex callback dependencies, custom CSS styling, enterprise authentication/scaling, or explicit control over what runs when. Streamlit is better for simple dashboards, prototypes, and ML demos.A.6 ★★★ | Evaluate
A colleague has a Streamlit dashboard that needs cross-filtering between three charts. Should they rewrite in Dash?
Guidance
Probably yes. Cross-filtering is awkward in Streamlit (requires session_state workarounds) and native in Dash. The rewrite is 1-3 days for a moderate dashboard, and the result will be cleaner and more maintainable. If the cross-filtering is only "nice to have," staying in Streamlit is reasonable. If it is critical to the workflow, migrate.Part B: Applied (10 problems)
B.1 ★☆☆ | Apply
Create a minimal Dash app with a title, a dropdown, and a graph.
Guidance
from dash import Dash, html, dcc, Input, Output
import plotly.express as px
app = Dash(__name__)
df = px.data.iris()
app.layout = html.Div([
html.H1("Iris"),
dcc.Dropdown(id="x-col", options=[{"label": c, "value": c} for c in df.columns[:4]], value="sepal_length"),
dcc.Graph(id="chart"),
])
@app.callback(Output("chart", "figure"), Input("x-col", "value"))
def update(col):
return px.histogram(df, x=col)
app.run(debug=True)
B.2 ★☆☆ | Apply
Add a second dropdown for y-axis and update the chart to a scatter plot.
Guidance
dcc.Dropdown(id="y-col", options=[...], value="sepal_width"),
@app.callback(Output("chart", "figure"), Input("x-col", "value"), Input("y-col", "value"))
def update(x, y):
return px.scatter(df, x=x, y=y, color="species")
B.3 ★★☆ | Apply
Use Dash Bootstrap Components to create a two-column layout.
Guidance
import dash_bootstrap_components as dbc
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = dbc.Container([
dbc.Row([
dbc.Col([dcc.Dropdown(id="filter", options=[...])], width=4),
dbc.Col([dcc.Graph(id="chart")], width=8),
]),
])
B.4 ★★☆ | Apply
Implement a callback where clicking a bar chart filters a scatter plot.
Guidance
@app.callback(
Output("scatter", "figure"),
Input("bar", "clickData"),
)
def filter_scatter(click_data):
if click_data is None:
return px.scatter(df, x="x", y="y")
category = click_data["points"][0]["x"]
return px.scatter(df[df["category"] == category], x="x", y="y")
B.5 ★★☆ | Apply
Use State to read a text input only when a button is clicked.
Guidance
@app.callback(
Output("output", "children"),
Input("submit", "n_clicks"),
State("text-input", "value"),
)
def handle(n_clicks, text):
if n_clicks is None:
return ""
return f"You said: {text}"
B.6 ★★☆ | Apply
Add auto-refresh every 10 seconds using dcc.Interval.
Guidance
dcc.Interval(id="interval", interval=10_000, n_intervals=0)
@app.callback(Output("chart", "figure"), Input("interval", "n_intervals"))
def refresh(n):
df = fetch_fresh_data()
return px.line(df, x="time", y="value")
B.7 ★★★ | Apply
Create a multi-page Dash app with two pages: Home and Data.
Guidance
# app.py
from dash import Dash, html, dcc, page_container, page_registry
app = Dash(__name__, use_pages=True, pages_folder="pages")
app.layout = html.Div([
dcc.Link("Home", href="/"),
dcc.Link("Data", href="/data"),
page_container,
])
# pages/home.py
from dash import register_page, html
register_page(__name__, path="/")
layout = html.Div([html.H1("Home")])
# pages/data.py
from dash import register_page, html
register_page(__name__, path="/data")
layout = html.Div([html.H1("Data")])
B.8 ★★★ | Apply
Build a pattern-matching callback that sums the values of dynamically-created sliders.
Guidance
from dash import ALL
app.layout = html.Div([
html.Div([
dcc.Slider(id={"type": "slider", "index": i}, min=0, max=100, value=50)
for i in range(5)
]),
html.Div(id="sum"),
])
@app.callback(
Output("sum", "children"),
Input({"type": "slider", "index": ALL}, "value"),
)
def sum_sliders(values):
return f"Sum: {sum(values)}"
B.9 ★★☆ | Apply
Implement a clientside callback that updates a label with the value of an input.
Guidance
app.clientside_callback(
"function(value) { return 'Value: ' + value; }",
Output("label", "children"),
Input("input", "value"),
)
B.10 ★★★ | Create
Build a complete sales dashboard with KPI cards, filters, and three cross-filtered charts.
Guidance
See Section 30.12 of the chapter for the full example. About 75 lines of Python with Dash Bootstrap Components.Part C: Synthesis (4 problems)
C.1 ★★★ | Analyze
Port a Streamlit dashboard to Dash and compare the line counts and complexity.
Guidance
Dash versions are typically 30-50% longer than equivalent Streamlit versions because of the explicit layout + callback separation. The complexity feels higher but scales better. For simple dashboards, Streamlit wins on brevity; for complex ones, Dash wins on maintainability.C.2 ★★★ | Evaluate
A Dash app has 20 callbacks that all depend on each other. What do you suggest?
Guidance
Use the callback graph visualizer to see the dependency structure. Consider consolidating related callbacks into single multi-output callbacks. Check for circular dependencies that Dash might be silently refusing to run. Consider whether a multi-page structure would isolate concerns. If the complexity is inherent, accept it and document the structure clearly.C.3 ★★★ | Create
Deploy a Dash app using Docker, Gunicorn, and Nginx.
Guidance
See Section 30.21 for the stack. Create a Dockerfile, a gunicorn command, and an nginx config. Test locally with `docker build` and `docker run` before deploying to a server. For production, add TLS certificates via Let's Encrypt.C.4 ★★★ | Evaluate
The chapter argues that "tool choice is a trade-off, not an absolute ranking." Apply this principle to your own workflow. When have you used Streamlit, Dash, or neither, and what drove the choice?
Guidance
Reflect on real projects. Common answers: Streamlit for quick demos and ML prototypes; Dash for client dashboards with custom styling; neither for analytical reports that are better as notebooks or PDFs. The exercise is to develop self-awareness about your own decision criteria.Chapter 31 moves from interactive dashboards to automated report generation.