Shinylive: Shiny in the Browser

Author

Mark Andrews

Abstract

Shinylive runs Shiny applications entirely inside the browser using WebAssembly. There is no R server: R itself executes in the browser via webR, a port of the R interpreter compiled to WebAssembly. The result is a static web page that can be hosted anywhere without an R server or any ongoing infrastructure cost.

What Shinylive is

Ordinary Shiny applications require a running R process on a server. When a user moves a slider, the browser sends the new value to the server, R re-runs the reactive expression, and the server sends back a new plot or table. This is simple to develop locally but requires managed server hosting for public deployment.

Shinylive eliminates the server entirely. The R interpreter is compiled to WebAssembly and runs inside the user’s browser alongside the application code. Reactive updates happen locally within the browser tab, with no network round-trip.

A Shinylive application is a collection of static files: HTML, JavaScript, and WebAssembly. Any web host that can serve static files can host it. GitHub Pages, which is free, works without any configuration beyond enabling the feature.

Limitations

Running R in the browser is a significant technical achievement and comes with real constraints that are worth understanding before choosing Shinylive over conventional deployment.

Startup time. The browser must download the WebAssembly R runtime and any required packages on the first load. This takes several seconds on a fast connection and considerably longer on a slow one. Subsequent visits may be faster if the browser has cached the assets.

Package availability. webR supports a curated subset of CRAN packages that have been compiled to WebAssembly. Most common data analysis and Shiny-related packages are available. Packages with compiled C or C++ code that has not been ported to WebAssembly may not be. The webR package repository at https://repo.r-wasm.org lists what is currently available.

No server-side operations. The application cannot connect to a database, call APIs that require server credentials, read from or write to the server file system, or do anything else that requires a running server process. Shinylive applications must be self-contained.

Performance. The WebAssembly R runtime is slower than native R for compute-intensive tasks. Applications that perform heavy simulation or model fitting will take noticeably longer to run in a browser than in RStudio.

Installing the shinylive package

install.packages("shinylive")

Exporting an application

Given an existing application in a directory called myapp/ containing an app.R file, export it to a static site:

shinylive::export(appdir = "myapp", destdir = "site")

This creates a site/ directory containing all the HTML, JavaScript, and WebAssembly files needed to run the application. Open site/index.html in a browser to test it locally.

You can serve the directory locally to simulate the deployed experience:

httpuv::runStaticServer("site")

Deploying to GitHub Pages

After exporting, deploy to GitHub Pages with these steps.

Push the site/ directory contents to the gh-pages branch of a GitHub repository:

git subtree push --prefix site origin gh-pages

In the repository settings on GitHub, navigate to Pages and set the source to the gh-pages branch. The application then becomes available at https://your-username.github.io/your-repo-name/.

Alternatively, configure a GitHub Actions workflow to run shinylive::export() and push the result automatically on each commit to main.

Embedding in a Quarto document

Shinylive integrates with Quarto, allowing a live Shiny application to be embedded directly in a rendered HTML document. Install the Quarto extension once per project:

quarto add quarto-ext/shinylive

Then use the {shinylive-r} chunk engine in your .qmd file:

```{shinylive-r}
#| standalone: true
library(shiny)

ui <- fluidPage(
  sliderInput("n", "Sample size", min = 10, max = 500, value = 100),
  plotOutput("hist")
)

server <- function(input, output) {
  output$hist <- renderPlot({
    hist(rnorm(input$n), col = "steelblue", border = "white")
  })
}

shinyApp(ui, server)
```

When the Quarto document is rendered to HTML, the application runs live in the browser without any R server. The #| standalone: true option indicates that the chunk contains a complete application rather than a fragment.

A complete example

The application below is suitable for Shinylive deployment. It uses only base R and the shiny package, both of which are fully supported by webR.

library(shiny)

ui <- fluidPage(
  titlePanel("Central limit theorem explorer"),
  sidebarLayout(
    sidebarPanel(
      sliderInput("n",    "Sample size",             min = 2,   max = 200,  value = 10),
      sliderInput("reps", "Number of replications",  min = 100, max = 2000, value = 500),
      selectInput("dist", "Population distribution",
                  choices = c("Normal"      = "norm",
                              "Exponential" = "exp",
                              "Uniform"     = "unif"))
    ),
    mainPanel(
      plotOutput("sdplot"),
      verbatimTextOutput("stats")
    )
  )
)

server <- function(input, output) {
  means <- reactive({
    draw <- switch(input$dist,
                   norm = function(n) rnorm(n),
                   exp  = function(n) rexp(n) - 1,
                   unif = function(n) runif(n) - 0.5)
    replicate(input$reps, mean(draw(input$n)))
  })

  output$sdplot <- renderPlot({
    m  <- means()
    se <- switch(input$dist,
                 norm = 1, exp = 1, unif = 1 / sqrt(3)) / sqrt(input$n)
    hist(m, freq = FALSE, col = "steelblue", border = "white",
         xlab = "Sample mean", main = "Sampling distribution of the mean")
    curve(dnorm(x, mean = 0, sd = se), add = TRUE, col = "firebrick", lwd = 2)
  })

  output$stats <- renderPrint({
    m <- means()
    cat(sprintf("Mean of sample means: %.4f\n", mean(m)))
    cat(sprintf("SD of sample means:   %.4f\n", sd(m)))
  })
}

shinyApp(ui, server)

This application will run without modification as a Shinylive export. For large values of reps, expect the WebAssembly runtime to be somewhat slower than native R, particularly on the first run before the browser has cached the runtime assets.

When to use Shinylive

Shinylive is a good choice when you want to share an application publicly without managing server infrastructure, when the application is genuinely self-contained (no database, no server-side files), and when startup time is acceptable for your audience.

It is less suitable for applications that do heavy computation, require packages not yet available in webR, or need to interact with external data sources. For those cases, conventional deployment to shinyapps.io or a dedicated server remains the right approach.