Interactive documents with Quarto

Beyond the basics of literate programming

Dr Nicola Rennie
RSS Conference 2026

About me

Data visualisation specialist, mainly using R, Python, and D3.


Background in statistics, operational research, and data science.


Author of several R packages (mainly for visualisation).


Co-author of Royal Statistical Society’s Best Practices for Data Visualisation guidance.

Grid of R package hex logos

Editable code

Static code blocks

```{r}
plot(mtcars$mpg, mtcars$wt)
```


plot(mtcars$mpg, mtcars$wt)

Interactive R code blocks

A little bit of webR magic

```{webr}
plot(mtcars$mpg, mtcars$wt)
```


  • Run R code directly in a browser
  • No R installation or server required
  • Try it at webr.sh

Interactive Python code blocks

```{pyodide}
2 + 2
```


  • Both R and Python live code work through Quarto Live extension
  • Interface very similar for both
  • Pre-install packages

Interactive git commands

  • A git-sandbox block in your Quarto document becomes a real Git repository running in a browser
  • Nothing is installed and nothing leaves the page.
  • Disappears on reload, so learners cannot break anything.
  • ryjohnson09.github.io/quarto-git-sandbox

Screenshot of git sandbox

Interactive charts

Interactive charts in Quarto

  • Put a {Shiny} app inside a Quarto document (requires a Shiny server)
  • Put a Shinylive app inside a Quarto document (webR / Pyodide magic)
  • Use Observable {ojs} code blocks
  • Add some HTML/CSS/JavaScript magic (not as tricky as it sounds!)

Shiny Quarto documents

---
title: "Old Faithful"
format: html
server: shiny
---

```{r}
sliderInput("bins", "Number of bins:", 
            min = 1, max = 50, value = 30)
plotOutput("distPlot")
```

```{r}
#| context: server
output$distPlot <- renderPlot({
  x <- faithful[, 2]  # Old Faithful Geyser data
  bins <- seq(min(x), max(x), length.out = input$bins + 1)
  hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
```

Shiny Quarto Documents

Screenshot of Shiny app

Shiny Quarto documents

  • Deploy Quarto documents with Shiny apps as you would normally deploy a Shiny app
  • Or use the shinylive Quarto extension to run in a web browser (using webR or Pyodide)
  • Both R and Python versions of shinylive available

htmlwidgets + HTMLtools

Add a dropdown:

library(htmltools)
options_vec <- unique(ggplot2::diamonds$cut)
names(options_vec) <- options_vec

dropdown_html <- tags$div(
  tags$select(
    id = "dropdown",
    lapply(
      names(options_vec),
      function(name) {
        tags$option(
          value =
            options_vec[[name]],
          name
        )
      }
    )
  )
)

htmlwidgets + HTMLtools

dropdown_html

Connecting to htmlwidgets

library(ggiraph)
library(ggplot2)
g_int <- ggplot() +
  geom_point_interactive(
    data = diamonds,
    mapping = aes(x = carat, y = price, colour = cut,
                  data_id = cut, tooltip = paste0("$", price))
  )
g_girafe <- girafe(ggobj = g_int, height_svg = 4.5)

Connecting to htmlwidgets

browsable(
  tagList(
    dropdown_html,
    g_girafe,
    tags$script(HTML("
      function filterByDropdown(selected) {
        document.querySelectorAll('[data-id]').forEach(el => {
          if (el.getAttribute('data-id') === selected) {
            el.setAttribute('style', 'display:block;');
          } else {
            el.setAttribute('style', 'display:none;');
          }
        });
      }

      document.getElementById('dropdown').addEventListener('change', function() {
        filterByDropdown(this.value);
      });

      // Run on initial load to filter by default selection
      document.addEventListener('DOMContentLoaded', function() {
        const initialSelected = document.getElementById('dropdown').value;
        filterByDropdown(initialSelected);
      });
    "))
  )
)

Connecting to htmlwidgets

Alternative output types

Carousels


First lemur
First lemur
Second lemur
Second lemur
Third lemur
Third lemur
Fourth lemur
Fourth lemur

Carousels


```carousel
![First lemur](images/lemur1.jpg)
![Second lemur](images/lemur2.jpg)
![Third lemur](images/lemur3.jpg)
![Fourth lemur](images/lemur4.jpg)
```

Scrollytelling

Screen recording of scrolly gif

Scrollytelling

---
title: My First Closeread
format: closeread-html
---

Hello World! Please read my Closeread story below.

:::{.cr-section}

Closeread enables scrollytelling.

Draw your readers attention with focus effects. @cr-features

:::{#cr-features}
1. Highlighting
2. Zooming
3. Panning
:::

:::

ggplot2 battles by Michael Lydeamore

Resources

nrennie.rbind.io/rss-2026-quarto