2  shiny(live)!

The Quarto doc will contain the shiny app using a code chunk. The type of code chunk should be {shinylive-r} (as opposed to {r}).

2.1 yml options

The main requirement for the code chunk yml is the option standalone: true.

For my application, I used the options:

```{shinylive-r}
#| label: fig-shiny-spline
#| viewerHeight: 500
#| standalone: true

The label doesn’t make it a figure as it normally would but see the section below for details regarding this (and details like captions, etc.).

The standard fig-height (and width) options don’t apply to shiny apps; viewerHeight is used instead.

You also have to add a Quarto filter in your _quarto.yml file:

filters:
  - shinylive

There is a bit more to say about formatting the chunk for Quarto but we’ll show that later.

2.2 Package declarations

In our chunk that contains the shiny app (i.e., fig-shiny-spline in this example), let’s make sure to install packages that we need (besides shiny):

webr::install("ggplot2")
webr::install("htmltools")   #<- only needed to use SVG images 

The base installation of webR includes its own webr utility package, so don’t worry about installing that.

2.3 User-interface function

The UI function is very simple and has no extra accouterments for shinylive

ui <- fluidPage(
  
  fluidRow(
    column(8, 
           sliderInput(
             "deg_free", 
             label = "Spline degrees of freedom:",
             min = 3L, value = 3L, max = 8L, step = 1L
           )
    ),
    
    imageOutput("spline_contours", height = "400px")
  )
)

2.4 The server function

This is where most of the customization happens.

server <- function(input, output, session) {
  
  # ------------------------------------------------------------------------
  # Input data from remote locations on GitHub
  
1  pred_path <-
    paste(                                                  
      "https://raw.githubusercontent.com",                  
      "topepo", "shinylive-in-book-test",                   
      "main", "predicted_values.RData",                     
      sep = "/"                                             
    )                                                       
  data_path <-                                              
    paste(                                                  
      "https://raw.githubusercontent.com",                  
      "topepo", "shinylive-in-book-test",                   
      "main", "sim_val.RData",                              
      sep = "/"                                             
    )                                                       
  
2  rdata_file <- tempfile()
  download.file(pred_path, destfile = rdata_file)           
  load(rdata_file)                                          
  download.file(data_path, destfile = rdata_file)           
  load(rdata_file)                                          
  
  # Set some ranges for the plot
  rngs <- list(A = c(-3.3, 3.3), B = c(-4.4, 4.4))
  
  output$spline_contours <-
    renderImage({
      
      preds <- predicted_values[predicted_values$deg_free == input$deg_free,]
      
      p <-
        ggplot(preds, aes(A, B)) +
        # Plot the validation set
        geom_point(
          data = sim_val, 
          aes(col = class, pch = class), 
          alpha = 1 / 2,
          cex = 3
        ) +
        # Show the class boundary
        geom_contour(
          aes(z = .pred_one),
          breaks = 1 / 2,
          linewidth = 3 / 2,
          col = "black"
        ) +
        # Formatting
        lims(x = rngs$A, y = rngs$B) +
        theme_bw() + 
        theme(legend.position = "top")
      
3      file <-
        htmltools::capturePlot(
          print(p), 
          tempfile(fileext = ".svg"),
          grDevices::svg,
          width = 4, 
          height = 4
        )
      list(src = file)
    }, 
4    deleteFile = TRUE)
}

app <- shinyApp(ui = ui, server = server)
1
This block sets up URLs pointing to the GitHub raw objects.
2
Both data sets are downloaded to a temporary file and loaded into the session.
3
Code allowing us to save the visualization as an SVG image. This is an aesthetic choice and unrelated to the point of this repo.
4
Clean up the SVG file passed between the server and UI.

2.5 The results

Our beautiful shiny app!

#| label: fig-shiny-spline
#| viewerHeight: 505
#| standalone: true
webr::install("ggplot2")
webr::install("htmltools")

library(ggplot2)
library(htmltools)

ui <- fluidPage(
  
  fluidRow(
    column(8, 
           sliderInput(
             "deg_free", 
             label = "Spline degrees of freedom:",
             min = 3L, value = 3L, max = 8L, step = 1L
           )
    ),
    
    imageOutput("spline_contours")
  )
)

server <- function(input, output, session) {
  
  # ------------------------------------------------------------------------
  # Input data from remote locations on GitHub
  
  pred_path <-                                              
    paste(                                                  
      "https://raw.githubusercontent.com",                  
      "topepo", "shinylive-in-book-test",                   
      "main", "predicted_values.RData",                     
      sep = "/"                                             
    )                                                       
  data_path <-                                              
    paste(                                                  
      "https://raw.githubusercontent.com",                  
      "topepo", "shinylive-in-book-test",                   
      "main", "sim_val.RData",                              
      sep = "/"                                             
    )                                                       
  
  rdata_file <- tempfile()                                  
  download.file(pred_path, destfile = rdata_file)           
  load(rdata_file)                                          
  download.file(data_path, destfile = rdata_file)           
  load(rdata_file)                                          
  
  # Set some ranges for the plot
  rngs <- list(A = c(-3.3, 3.3), B = c(-4.4, 4.4))
  
  output$spline_contours <-
    renderImage({
      
      preds <- predicted_values[predicted_values$deg_free == input$deg_free,]
      
      p <-
        ggplot(preds, aes(A, B)) +
        # Plot the validation set
        geom_point(
          data = sim_val, 
          aes(col = class, pch = class), 
          alpha = 1 / 2,
          cex = 3
        ) +
        # Show the class boundary
        geom_contour(
          aes(z = .pred_one),
          breaks = 1 / 2,
          linewidth = 3 / 2,
          col = "black"
        ) +
        # Formatting
        lims(x = rngs$A, y = rngs$B) +
        theme_bw() + 
        theme(legend.position = "top")
      
      file <- 
        htmltools::capturePlot(
          print(p), 
          tempfile(fileext = ".svg"),
          grDevices::svg,
          width = 4, 
          height = 4
        )
      list(src = file)
    }, 
    deleteFile = TRUE)               
}

app <- shinyApp(ui = ui, server = server)
Figure 2.1: A visualization of the class boundary for different numbers of degrees of freedom for natural spline features in A and B.

You can reference Figure 2.1 in the usual way (via @fig-shiny-spline).

2.6 Making the shiny app a figure

As previously mentioned, following the usual convention of using a fig- prefix for a chunk will not make the shiny app a figure. However, the 1.4 release of Quarto (pre-release version as of this writing) has a new feature to make anything a specific type of content (such as a figure).

We use the ::: syntax to declare the figure and any critical options. In my case above, this was

::: {#fig-shiny-spline}

::: {.figure-content}

```{shinylive-r}
#| label: fig-shiny-spline
#| viewerHeight: 500
#| standalone: true

<-- my shiny app code here-->

app <- shinyApp(ui = ui, server = server)
```
:::

A visualization of the class boundary for different numbers of degrees of 
freedom for natural spline features in `A` and `B`.   

:::

You probably don’t have to use the same chunk name ({#fig-shiny-spline}) in two places; this is the more important place to specify it. I like having them the same.