Shiny without Boundaries
One App, Multiple Destinations
James Balamuta
Guest Lecture STAT 447 @ UIUC
April 9th, 2025 @ 4 PM CDT / 6 PM PDT
In this lecture, we’ll have a conservation about:
These examples (minus the last one) are all deployed using Shinylive.
Advantages:
Considerations:
Advantages:
Considerations:
Advantages:
Considerations:
Advantages:
Considerations:
🔗 https://shinylive.io/r/editor
The code that makes up the app is encoded in the URL. As a result, you can copy and paste it to share with others.
pkg_db <- tools::CRAN_package_db()
shiny_pkg_dependencies <- tools::package_dependencies(
packages = "shiny",
recursive = TRUE,
db = pkg_db
)
shiny_pkg_dependencies
#> $shiny
#> [1] "methods" "utils" "grDevices" "httpuv" "mime"
#> [6] "jsonlite" "xtable" "fontawesome" "htmltools" "R6"
#> [11] "sourcetools" "later" "promises" "tools" "crayon"
#> [16] "rlang" "fastmap" "withr" "commonmark" "glue"
#> [21] "bslib" "cachem" "ellipsis" "lifecycle" "base64enc"
#> [26] "jquerylib" "memoise" "sass" "digest" "Rcpp"
#> [31] "cli" "magrittr" "stats" "graphics" "fs"
#> [36] "rappdirs"
quarto publish
ui.R
and server.R
We’ll begin by creating a Shiny app, converting it to a Shinylive app, and deploying it to GitHub Pages.
We’ll use the following Shiny app that randomly samples from a normal distribution and plots the histogram and density curve as an example.
app.R
library(shiny)
library(bslib)
ui <- page_sidebar(
sidebar = sidebar(
numericInput("n", "Sample size", 100),
checkboxInput("pause", "Pause", FALSE)
),
plotOutput("plot")
)
server <- function(input, output) {
data <- reactive({
input$resample
if (!isTRUE(input$pause)) {
invalidateLater(1000)
}
rnorm(input$n)
})
output$plot <- renderPlot({
hist(data(), freq = FALSE)
lines(density(data()))
})
}
shinyApp(ui, server)
The shinylive
package can be installed from CRAN by using:
You do not need any other packages to create Shinylive apps.
To convert a Shiny app to a Shinylive app, use the shinylive::export
function:
The first argument is the directory containing the Shiny app, and the second is the output directory. We’re assuming the app is in the current working directory, e.g. "."
and the output directory is "_site"
.
To view the app locally, we’ll need to use a live (local) server. One option is to use the httpuv
package:
This will start a local server at http://
followed by an IP address and port number.
Directly opening the index.html
file in a browser will not work due to security restrictions. Also, do not use shiny::runApp()
to view Shinylive apps as it will not work.
Apps are converted from .R
and stored in app.json
with the following structure:
[
{
"name": "app.R",
"content": "library(shiny)\nui <- fluidPage(...)\nserver <- function(input, output) {...}\nshinyApp(ui, server)",
"type": "text"
}
]
Components:
name
: File name (app.R, ui.R, server.R, etc.)content
: File contents (escaped)type
: Usually “text”Steps to convert a Shinylive app back to a Shiny app:
app.json
file
/app.json
to download.jsonlite::fromJSON()
content
field for each file{peeky} R package automates this process.
There Are …
No Secrets
We can deploy the app to GitHub Pages by following these steps:
.
├── .github/workflows/
│ └── build-and-deploy.yml
├── README.md
├── DESCRIPTION
└── app.R
Make sure to enable GitHub Pages in the repository settings with Enforce HTTPS
checked!
build-and-deploy.yml
on:
push:
branches: [main, master]
name: shinylive-deploy
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
pages: write
id-token: write
steps:
- uses: actions/checkout@v4
- uses: r-lib/actions/setup-r@v2
- name: "Setup R dependency for Shinylive App export"
uses: r-lib/actions/setup-r-dependencies@v2
with:
packages:
cran::shinylive,
local::.
- name: Export app
run: |
shinylive::export(".", "_site")
shell: Rscript {0}
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
retention-days: 1
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
push
to main
or master
shinylive
package_site
directoryAvoids retaining artifacts in main repository for faster clones!
Shinylive apps can be embedded in Quarto documents using the shinylive
Extension.
Download and install the Quarto extension into your Quarto project by typing in Terminal:
This will download the extension into the _extensions
folder.
Quarto extensions are per-project and require either specifying the filter in the YAML header or in the _quarto.yaml
project configuration file.
Add to YAML header:
Use the shinylive-r
custom code cell in document:
We can publish the Quarto document to a static website using the quarto publish
command.
on:
push:
branches: [main, master]
release:
types: [published]
workflow_dispatch: {}
name: demo-website
jobs:
demo-website:
runs-on: ubuntu-latest
concurrency:
group: quarto-website-${{ github.event_name != 'pull_request' || github.run_id }}
permissions:
contents: read
pages: write
id-token: write
steps:
- name: "Check out repository"
uses: actions/checkout@v4
- name: "Setup pandoc"
uses: r-lib/actions/setup-pandoc@v2
- name: "Setup R"
uses: r-lib/actions/setup-r@v2
- name: "Setup R dependencies for Quarto's knitr engine"
uses: r-lib/actions/setup-r-dependencies@v2
with:
packages:
## Pin version to ensure consistency
cran::shinylive@0.2.0
any::knitr
any::rmarkdown
any::downlit
any::xml2
- name: "Set up Quarto"
uses: quarto-dev/quarto-actions/setup@v2
- name: "Render working directory"
uses: quarto-dev/quarto-actions/render@v2
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
retention-days: 1
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
Similar to the Shinylive GitHub Actions workflow, but with Quarto-specific steps:
Ensure you have a _quarto.yml
configuration file in the repository!
Advantages:
Considerations:
Packages are the fundamental units of reproducible R code. They include reusable R functions, the documentation that describes how to use them, and sample data.
– Hadley Wickham and Jennifer Bryan in R packages (2e)
Advantages:
Considerations:
This is popular for Shiny dashboards.
You are better off deploying a Shiny app as a standalone app and linking to it from a Quarto document if using in a report.
You must have a Shiny server (compute server) running to use Quarto with Shiny outside of the Shinylive context.
Document YAML Options:
format: html
- Output formatserver: shiny
- Enables interactivityThis code:
**context: server**
execution contextThe slider:
"n"
By default, the context chosen is that of the UI. Hence, we do not need to specify it.
The output:
panel: fill
makes the plot fill available spaceplotOutput
displays our reactive scatter plot---
title: "Interactive Report with Embedded Shiny"
format: html
server: shiny
---
## Interactive Analysis
```{r}
#| context: server
dataset <- reactive({
mtcars[sample(nrow(mtcars), input$n), ]
})
# Define output
output$scatterPlot <- renderPlot({
ggplot(dataset(), aes(x = wt, y = mpg)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE)
})
```
```{r}
sliderInput("n", "Number of cars:",
min = 5, max = 32, value = 20)
```
```{r}
#| context: server
#| panel: fill
plotOutput("scatterPlot")
```
Advantages:
Considerations:
Thank you! Any questions?
This work is licensed under CC BY-NC-ND 4.0
Shiny without Boundaries • James Balamuta • © 2025