Last week an exciting new addition to the R ecosystem was announced: webR. webR lets you run R from inside your browser, without installing R1. This is a really exciting development for educational materials, because it makes it possible to provide interactive materials where students can modify, create, and run code right inside a web page with zero setup. While it has been possible to do this in the past it required that the website be setup to run code on a server, which both required expertise that a lot of folks teaching R don’t have and introduced a lot of complexities related to things like security and server costs. With webR that all goes away.
One of the key recent tools that makes developing lessons (and lots of other things) is Quarto, which lets you write documents in markdown with embedded code blocks and then automatically turn them into documents, like web pages. And almost as soon as the first release of webR was announced there was an extension to use it as part of Quarto. So I spent a few hours this weekend seeing how far I could get with putting together some demo interactive lessons.
Results
If you just want to see what you can do you can go checkout the interactive lessons demo site. There are example lessons for basic R, dplyr, and ggplot. Give webR a few seconds to load things, the click on some Run Code buttons, change the code in the box, and then click Run Code again. You can do whatever you want, it’s running on your local machine right in the browser.
Details
You can see the current state of the material producing the demo site in the GitHub repository.
Installation
You need to have Quarto installed to start. Then install the webR Quarto extension.
quarto install extension coatless/quarto-webr
Basics
Inside a quarto document where you want to use webR you need to do two things. First, add this to you YAML front matter:
engine: knitr
filters:
- webr
Second, create code blocks for webR using:
```{webr}
# Your code
```
Then just preview or render the quarto file and you should be able to run and modify those code cells right on the resulting webpage.
You can do everything you would in a normal R script, including downloading files from urls and then loading them into R (there’s an example of this in the dplyr demo).
Adding packages
Things get a little trickier if you need to install packages. The simplest option is to have the user install them using a webR code block. You can’t do this using install.packages, you need to use webR’s own package system. To install dplyr you run
webr::install('dplyr')
This is fine, but it might be a little confusing since it’s not how you install packages normally, and you often want to jump straight to teaching without the boiler plate of package install.
Building on the webR docs and a great example by boB Rudis2, I used the following piece of Javascript, which can be copied and pasted directly into any quarto page:
<html>
<script type="module">
//=== WebR setup ===
import { WebR } from 'https://webr.r-wasm.org/latest/webr.mjs';
globalThis.webR = new WebR();
await globalThis.webR.init();
globalThis.webRCodeShelter = await new globalThis.webR.Shelter();
await globalThis.webR.installPackages(['dplyr', 'readr'])
</script>
</html>
Just change the packages on the installPackages
line to what you need. Not all R packages are available, but the main TidyVerse packages are.
Making things faster (& more secure)
Once you start installing packages things start to get a little slow. You can make things a lot faster3 (and more secure) by following the webR docs strong suggestion to “To ensure a web page is cross-origin isolated, serve the page with both the COOP and COEP HTTP headers set”. The docs provide an example for doing it locally. If you’re deploying using Netlify, a common way of putting Quarto sites on the web, you can do this by creating a file named _headers
in your deploy directory that contains the following:
/*
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Other bits and pieces
With the headers properly configured I found installing and loading dplyr and readr to both be fast enough that most folks won’t notice much. ggplot2, on the other hand, is a heavier dependency and it was slow enough that I felt the need to add a warning to the top of the page to avoid confusion. That said, once it’s loaded everything is super snappy, so it’s just a question of getting the student/user to understand that there will be a little bit of a lag at the very beginning.
I’ve also started playing with the use of callout boxes with webR code blocks for providing an in-browser way to work on exercises. Here’s what the example in the ggplot demo lesson looks like:

The student can type their own code directly into the code box, while easily scrolling up in the web page to remind themselves of what they’ve just learned. If they want to check if they have the right idea they can start by looking at the expected result (which expands when clicked to show the desired figure) or look at the solution code that the lesson designer created. Side note, even for graded material in my courses I always give the students access to the expected result so that they know what they are supposed to be producing. I’ve found it really helpful for avoiding confusion about the intent of questions.
Summary
After playing around with webR + Quarto for a day or so I think this (and related systems in other languages) are going to provide a lot of really cool opportunities for making it easy to deploy interactive lesson material both for formal classes and most importantly for open educational resources. I definitely recommend checking out this approach and I’m happy to answer questions in the comments if you decide to give it a try.
A huge thanks to all of the folks making these incredible tools including George Stag and Lionel Henry for webR and James Balamuta for the Quarto extension. Also a huge thanks to boB Rudis for all his work sharing these tools and how to use them.
Read more
Footnotes
1This is part of a larger set of work to bring more meaningful computing to the browser including implementations for Python.
2I couldn’t have done most of this without boB’s excellent posts on Mastodon. Checkout his webR blog post for a full write up.