Dashboards with Observable JS


Quarto includes native support for Observable JS, a set of enhancements to vanilla JavaScript created by Mike Bostock (also the author of D3). Observable JS is distinguished by its reactive runtime, which is especially well suited for interactive data exploration and analysis.

The creators of Observable JS (Observable, Inc.) run a hosted service at https://observablehq.com/ where you can create and publish notebooks. Additionally, you can use Observable JS (“OJS”) in standalone documents and websites via its core libraries. Quarto uses these libraries along with a compiler that is run at render time to enable the use of OJS within Quarto documents.


Quarto Dashboards are a great way to present interactive OJS visualizations. Below we’ll provide a complete example which will give you a high level view of the basics. If you want to learn more, please see the complete documentation on Using OJS with Quarto.

This example covers many of the techniques you’ll use when creating dashboards with OJS, including reactive calculations and more advanced layout constructs like sidebars and tabsets. Here is the interactive document we’ll be building:

A screenshot of a Palmer Penguins dashboard. The navigation bar shows two pages: Plot (active) and Data. On the left a sidebar with an image of penguins and two inputs: a range input for Bill length; and a set of checkboxes for Islands. On the right a plot with histgrams of body mass facetted by sex and species, with bars colored by species.

The source code for this dashboard is below. Note that we add the
//| output: false option to the first cell: this is to designate the cell as having only intermediate computations (so it should not be turned into a card in the dashboard layout).

Click on the numbers on the far right for additional explanation of syntax and mechanics)

title: "Palmer Penguins"
author: "Cobblepot Analytics"
format: dashboard

//| output: false

data = FileAttachment("penguins.csv")
  .csv({ typed: true })

filtered = data.filter(function(penguin) {
  return bill_length_min < penguin.bill_length_mm &&

# {.sidebar}


viewof bill_length_min = Inputs.range(
  [32, 50], 
  {value: 35, step: 1, label: "Bill length (min):"}
viewof islands = Inputs.checkbox(
  ["Torgersen", "Biscoe", "Dream"], 
  { value: ["Torgersen", "Biscoe", "Dream"], 
    label: "Islands:"

# Plot

    {y: "count"}, 
    {x: "body_mass_g", fill: "species", thresholds: 20}
    facet: {
      data: filtered,
      x: "sex",
      y: "species",
      marginRight: 80
    marks: [

# Data

We set output: false to indicate that this cell includes only intermediate calculations and should not have its contents printed.
We read the raw penguins dataset from a CSV when the page loads.
filtered is a value that is automatically recomputed when variables declared with viewof change (in this case bill_length_min and islands).
Create global sidebars by adding the .sidebar class to a level 1 header. Sidebars can include code cells as well as images, narrative, and links.
Here we define our inputs using viewof so that the filtered dataset is automatically recomputed when they change.
Level 1 headings (here # Plots and # Data) create pages within the dashboard.
The plot is automatically redrawn whenever the filtered dataset changes.
The tabular data display is automatically refreshed whenever the filtered dataset changes.

Learning More

To learn more about using OJS with Quarto, see the following articles:

Input describes various ways to layout inputs (sidebars, input panels, attaching inputs directly to cards, etc.).

Using OJS provides an introduction and overview of other topics.

OJS Libraries covers using standard libraries and external JavaScript libraries.

OJS Data Sources outlines the various ways to read and pre-process data.

OJS Cells goes into more depth on cell execution, output, and layout.

OJS Code Reuse delves into ways to re-use OJS code across multiple documents.