10 Using shinylive
shinylive is an extension and R package that allows for embedding a serverless Shiny application in a Quarto page, as we do in Figure 10.1.
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| viewerHeight: 600
library(shiny)
library(bslib)
# Define UI for app that draws a histogram ----
ui <- page_sidebar(
sidebar = sidebar(open = "open",
numericInput("n", "Sample count", 100),
checkboxInput("pause", "Pause", FALSE),
),
plotOutput("plot", width=500)
)
server <- function(input, output, session) {
data <- reactive({
input$resample
if (!isTRUE(input$pause)) {
invalidateLater(1000)
}
rnorm(input$n)
})
output$plot <- renderPlot({
op <- par(cex = 0.5)
hist(data(),
breaks = 40,
xlim = c(-2, 2),
ylim = c(0, 1),
lty = "blank",
xlab = "value",
freq = FALSE,
main = ""
)
x <- seq(from = -2, to = 2, length.out = 500)
y <- dnorm(x)
lines(x, y, lwd=1.5)
lwd <- 5
abline(v=0, col="red", lwd=lwd, lty=2)
abline(v=mean(data()), col="blue", lwd=lwd, lty=1)
legend(legend = c("Normal", "Mean", "Sample mean"),
col = c("black", "red", "blue"),
lty = c(1, 2, 1),
lwd = c(1, lwd, lwd),
x = -2,
y = 1
)
}, res=140)
}
# Create Shiny app ----
shinyApp(ui = ui, server = server)
shinylive example showing random samples from a Normal distribution. Click the ‘Pause’ checkbox to freeze on a sample. Use the ‘Sample count’ option to choose the number of samples. The black line shows the Normal distribution being sampled from, and the dashed red line shows the population mean. The solid blue line shows the sample mean. The grey histogram shows the sample.
```{shinylive-r}
#| standalone: true
#| viewerHeight: 600
{CODE GOES HERE}
```library(shiny)
library(bslib)
# Define UI for app that draws a histogram ----
ui <- page_sidebar(
sidebar = sidebar(open = "open",
numericInput("n", "Sample count", 100),
checkboxInput("pause", "Pause", FALSE),
),
plotOutput("plot", width=500)
)
server <- function(input, output, session) {
data <- reactive({
input$resample
if (!isTRUE(input$pause)) {
invalidateLater(1000)
}
rnorm(input$n)
})
output$plot <- renderPlot({
op <- par(cex = 0.5)
hist(data(),
breaks = 40,
xlim = c(-2, 2),
ylim = c(0, 1),
lty = "blank",
xlab = "value",
freq = FALSE,
main = ""
)
x <- seq(from = -2, to = 2, length.out = 500)
y <- dnorm(x)
lines(x, y, lwd=1.5)
lwd <- 5
abline(v=0, col="red", lwd=lwd, lty=2)
abline(v=mean(data()), col="blue", lwd=lwd, lty=1)
legend(legend = c("Normal", "Mean", "Sample mean"),
col = c("black", "red", "blue"),
lty = c(1, 2, 1),
lwd = c(1, lwd, lwd),
x = -2,
y = 1
)
}, res=140)
}
# Create Shiny app ----
shinyApp(ui = ui, server = server)10.1 The fundamentals of a Shiny app
We don’t have space here for a tutorial on how to use Shiny. There is a learning curve, and we can recommend a number of online resources for you to get up to speed with this package, including:
To implement a Shiny app, you need to define three things in your R code:
- a user interface object that defines how the user interacts with the app (sliders, checkboxes, plots, etc.)
- a server function that returns values to be displayed by the user interface
- a call to the
shinyApp()function
library(shiny) # of course, you have to make the Shiny package available
ui <- {CODE DEFINING USER INTERFACE}
server <- function(input, output, session) {CODE DEFINING WHAT HAPPENS}
shinyApp(ui = ui, server = server)10.2 What you need to do special for shinylive
Under normal circumstances, the Shiny code with the three elements above would run on a Shiny server, and you wouldn’t need to do much else. But with shinylive and Quarto you need to place the app on the page, and tell Quarto that you want to use the shinylive filter. Firstly, the header of the page should be defined in YAML as, e.g.:
---
title: "Using `shinylive`"
filters:
- shinylive
format:
html:
css: assets/fix_editor.css
---The format: section is required to fix a layout problem with r-shinylive interactive editors, described in Section 10.3.
This requires a new kind of fenced block stating that it is {shinylive-r}:
```{shinylive-r}
{CODE GOES HERE}
```and there are new block-level arguments controlling on-page display, e.g.
```{shinylive-r}
#| standalone: true
#| viewerHeight: 600
{CODE GOES HERE}
```The standalone:true setting is required for the compiled app to run in your public repository. This setting tells shinylive that the entire app is contained within the fenced code block.
10.2.1 Example
The shinylive example in Figure 10.2 below is fairly short but includes some important points of difference from Figure 10.1.
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| viewerHeight: 600
library(shiny)
library(ggplot2)
library(DT)
if (FALSE) {
library(munsell)
}
ui <- fluidPage(
plotOutput("plot", brush = "plot_brush"),
DTOutput("table")
)
server <- function(input, output, session) {
output$plot <- renderPlot(
ggplot(mtcars) +
geom_point(aes(x = mpg, y = disp))
)
output$table <- renderDT({
brushedPoints(mtcars, input$plot_brush)
})
}
shinyApp(ui = ui, server = server)
shinylive example using ggplot2, DT, and allowing for interactive selection of points from the graph.
```{shinylive-r}
#| standalone: true
#| viewerHeight: 600
# Import required libraries
library(shiny)
library(ggplot2)
library(DT)
# ggplot2 will not work in shinylive without this - see callout
if (FALSE) {
library(munsell)
}
# Define the user interface
ui <- fluidPage(
plotOutput("plot", brush = "plot_brush"),
DTOutput("table")
)
# Define the server code
server <- function(input, output, session) {
output$plot <- renderPlot(
ggplot(mtcars) +
geom_point(aes(x = mpg, y = disp))
)
output$table <- renderDT({
brushedPoints(mtcars, input$plot_brush)
})
}
# Run the shinyapp
shinyApp(ui = ui, server = server)
```# Import required libraries
library(shiny)
library(ggplot2)
library(DT)
# ggplot2 will not work in shinylive without this - see callout
if (FALSE) {
library(munsell)
}
# Define the user interface
ui <- fluidPage(
plotOutput("plot", brush = "plot_brush"),
DTOutput("table")
)
# Define the server code
server <- function(input, output, session) {
output$plot <- renderPlot(
ggplot(mtcars) +
geom_point(aes(x = mpg, y = disp))
)
output$table <- renderDT({
brushedPoints(mtcars, input$plot_brush)
})
}
# Run the shinyapp
shinyApp(ui = ui, server = server)ggplot2 with shinylive
There is a known issue with ggplot2 and shinylive such that no output is produced due to a missing suggested dependency. A workaround is indicated at this StackOverflow page :
library(ggplot2)
if (FALSE) {
library(munsell)
}10.3 Adding the R editor
The examples above allow for user interaction, but not user editing of the code. With shinylive it is possible to include an editor so that users can modify the code of the app directly, in their own browser.
To do this, include the code block setting: #| components: [editor, viewer], as in Figure 10.3.
Quarto book pages are tall rather than wide, so it is often helpful to stack the editor and viewer vertically, with the options:
#| components: [editor, viewer]
#| layout: verticalThe layout for the r-shinylive editor inherits text alignment from the surrounding <div> and can be misaligned. This book template includes a .css file that fixes this, and which must be included in the YAML header for the page:
format:
html:
css: assets/fix_editor.css#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| viewerHeight: 600
#| components: [viewer, editor]
#| layout: vertical
library(shiny)
library(ggplot2)
library(DT)
if (FALSE) {
library(munsell)
}
ui <- fluidPage(
plotOutput("plot", brush = "plot_brush"),
DTOutput("table")
)
server <- function(input, output, session) {
output$plot <- renderPlot(
ggplot(mtcars) +
geom_point(aes(x = mpg, y = disp))
)
output$table <- renderDT({
brushedPoints(mtcars, input$plot_brush)
})
}
shinyApp(ui = ui, server = server)
shinylive example using ggplot2, DT, and allowing for interactive selection of points from the graph.
```{shinylive-r}
#| standalone: true
#| viewerHeight: 600
#| components: [viewer, editor]
#| layout: vertical
# Import required libraries
library(shiny)
library(ggplot2)
library(DT)
# ggplot2 will not work in shinylive without this - see callout
if (FALSE) {
library(munsell)
}
# Define the user interface
ui <- fluidPage(
plotOutput("plot", brush = "plot_brush"),
DTOutput("table")
)
# Define the server code
server <- function(input, output, session) {
output$plot <- renderPlot(
ggplot(mtcars) +
geom_point(aes(x = mpg, y = disp))
)
output$table <- renderDT({
brushedPoints(mtcars, input$plot_brush)
})
}
# Run the shinyapp
shinyApp(ui = ui, server = server)
```# Import required libraries
library(shiny)
library(ggplot2)
library(DT)
# ggplot2 will not work in shinylive without this - see callout
if (FALSE) {
library(munsell)
}
# Define the user interface
ui <- fluidPage(
plotOutput("plot", brush = "plot_brush"),
DTOutput("table")
)
# Define the server code
server <- function(input, output, session) {
output$plot <- renderPlot(
ggplot(mtcars) +
geom_point(aes(x = mpg, y = disp))
)
output$table <- renderDT({
brushedPoints(mtcars, input$plot_brush)
})
}
# Run the shinyapp
shinyApp(ui = ui, server = server)What happens if you change line 17 of the code in the editor to:
geom_point(aes(x = mpg, y = disp, color=as.factor(cyl)))and run the code (click on the triangle, or press Shift-Return/Cmd-Shift-Return)
10.4 Installing the shinylive extension
For local use and development you will need to install the shinylive package:
install.packages("shinylive")To install the shinylive Quarto extension, use the commmand:
quarto add quarto-ext/shinyliveThe shinylive extension is installed as part of this template, and the shinylive package is installed via the DESCRIPTION file. This extension should work automatically in the rendered GitHub pages.