Dashboards

CIPPCD 2025: Dynamic Reproducible
Documents for Public Policy with Quarto

Julia Silge

Posit PBC

Hello!

@juliasilge

@juliasilge.com

youtube.com/juliasilge

juliasilge.com

Anatomy of a Quarto dashboard

Components

  1. Metadata: YAML

  2. Text: Markdown, likely placed in cards

  3. Code: Executed via jupyter (or knitr), automatically placed in cards

Weave it all together, and you have a beautiful, functional dashboard!

Our turn

Activity

Let’s review the components of hello-penguins-dashboard.qmd.

Notebook ➝ Dashboard

hello-penguins-dashboard.qmd
---
title: Hello, penguins!
format: dashboard
---

# notebook content goes here...

Dashboard Components

  1. Navigation Bar and Pages — Icon, title, and author along with links to sub-pages (if more than one page is defined).

  2. Sidebars, Rows & Columns, and Tabsets — Rows and columns using markdown heading (with optional attributes to control height, width, etc.). Sidebars for interactive inputs. Tabsets to further divide content.

  3. Cards (Plots, Tables, Value Boxes, Content) — Cards are containers for cell outputs and free form markdown text. The content of cards typically maps to cells in your notebook or source document.

All of these components can be authored and customized within notebook UI or plain text qmd.

Layout: Rows

---
title: "Focal (Top)"
format: dashboard
---
    
## Row {height=70%}

```{python}
```

## Row {height=30%}

```{python}
```

```{python}
```

Layout: Columns

---
title: "Focal (Top)"
format: 
  dashboard:
    orientation: columns
---
    
## Column {width=60%}

```{python}
```

## Column {width=40%}

```{python}
```

```{python}
```

Tabsets

---
title: "Palmer Penguins"
format: dashboard
---
    
## Row

```{python}
```

## Row {.tabset}

```{python}
#| title: Chart 2
```

```{python}
#| title: Chart 3
```

Plots

plotly

```{python}
#| title: GDP and Life Expectancy
import plotly.express as px
df = px.data.gapminder()
px.scatter(
  df, x="gdpPercap", y="lifeExp", 
  animation_frame="year", animation_group="country",
  size="pop", color="continent", hover_name="country", 
  facet_col="continent", log_x=True, size_max=45, 
  range_x=[100,100000], range_y=[25,90]
)
```

Plots

ipyleaflet

```{python}
#| title: "World Map"
#| padding: 0px
from ipyleaflet import Map, basemaps, basemap_to_tiles
Map(basemap=basemap_to_tiles(basemaps.OpenStreetMap.Mapnik),
    center=(48.204793, 350.121558), zoom=2)
```

Tables

tabulate

```{python}
from tabulate import tabulate
from IPython.display import Markdown
Markdown(tabulate(penguins, showindex=False)}
```

itables

```{python}
from itables import show
show(penguins)
```

Value Boxes

## Row

```{python}
#| component: valuebox
#| title: "Current Price"
dict(icon = "currency-dollar",
     color = "secondary",
     value = get_price(data))
```

```{python}
#| component: valuebox
#| title: "Change"
change = get_change(data)
dict(value = change['amount'],
     icon = change['icon'],
     color = change['color']) 
```

Text Content

## Column

```{python}
#| title: Population
px.area(df, x="year", y="pop", 
        color="continent", 
        line_group="country")
```

```{python}
#| title: Life Expectancy
px.line(df, x="year", y="lifeExp", 
        color="continent", 
        line_group="country")
```

::: {.card}
Gapminder combines data from multiple sources
into unique coherent time-series that can’t be
found elsewhere. Learn more about the Gampminder
dataset at <https://www.gapminder.org/data/>.
:::

Expanding Cards

Cards provide an Expand button which appears at bottom right on hover:

Dashboard Deployment

Dashboards are typically just static HTML pages so can be deployed to any web server or web host.

Approach When?
Static Rendered a single time (e.g. when underlying data won’t ever change)
Scheduled Rendered on a schedule (e.g. via cron job) to accommodate changing data
Parameterized Fully interactive dashboard using Shiny (requires a server for deployment)

Parameterized Dashboards

Add a parameters tag to the first cell (based on papermill) :

```{python}
#| tags: [parameters]
ticker = "BA"
```

Use the -P command line option to vary the parameter:

quarto render dashboard.qmd -P ticker:GOOG

Interactive Dashboards

https://quarto.org/docs/dashboards/interactivity/shiny-python/

  • For interactive exploration, some dashboards can benefit from a live Python backend

  • To do this with Quarto Dashboards, add interactive Shiny components

  • Deploy with or without a server!

Deployment of Interactive Dashboards

Server

Cloud

shinyapps.io, HuggingFace Spaces, or roll your own

On-Prem

Shiny Server (open source, Posit Connect (commercial), or roll your own


Serverless

Using Pyodide such as with this example

Wrap up

Learn more

quarto.org/docs/dashboards

Thanks!

@juliasilge

@juliasilge.com

youtube.com/juliasilge

juliasilge.com