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 ----
<- page_sidebar(
ui sidebar = sidebar(open = "open",
numericInput("n", "Sample count", 100),
checkboxInput("pause", "Pause", FALSE),
),plotOutput("plot", width=500)
)
<- function(input, output, session) {
server <- reactive({
data $resample
inputif (!isTRUE(input$pause)) {
invalidateLater(1000)
}rnorm(input$n)
})
$plot <- renderPlot({
output<- par(cex = 0.5)
op
hist(data(),
breaks = 40,
xlim = c(-2, 2),
ylim = c(0, 1),
lty = "blank",
xlab = "value",
freq = FALSE,
main = ""
)
<- seq(from = -2, to = 2, length.out = 500)
x <- dnorm(x)
y lines(x, y, lwd=1.5)
<- 5
lwd 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
<- {CODE DEFINING USER INTERFACE}
ui
<- function(input, output, session) {CODE DEFINING WHAT HAPPENS}
server
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
<- fluidPage(
ui plotOutput("plot", brush = "plot_brush"),
DTOutput("table")
)
# Define the server code
<- function(input, output, session) {
server $plot <- renderPlot(
outputggplot(mtcars) +
geom_point(aes(x = mpg, y = disp))
)$table <- renderDT({
outputbrushedPoints(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:
[editor, viewer]
#| components: #| layout: vertical
The 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
<- fluidPage(
ui plotOutput("plot", brush = "plot_brush"),
DTOutput("table")
)
# Define the server code
<- function(input, output, session) {
server $plot <- renderPlot(
outputggplot(mtcars) +
geom_point(aes(x = mpg, y = disp))
)$table <- renderDT({
outputbrushedPoints(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/shinylive
The 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.