Observable for R users
Observable is a JavaScript-based programming framework for data exploration and visualisation, which is popular for creating interactive charts and dashboards. This blog post demonstrates why and how R users can integrate Observable into their existing R workflows.
April 1, 2025
Observable is a JavaScript-based reactive programming environment, commonly used for interactive data exploration, visualisation, and dashboards. You can create and publish Observable notebooks at observablehq.com (which is also a great place to look for inspiration and support) or you can use Observable Javascript in standalone documents and websites.
So why would an R user want to learn or use Observable? Although there are several different packages for creating interactive charts directly in R, these can sometimes be hard to customise, load slowly, or require an R server for deployment. With Observable, you can use different JavaScript libraries for advanced customisation (though the learning curve might be quite steep for some). Since it’s JavaScript-based, there’s no need for a server to run code, and its reactive model and efficient rendering in a browser, means that its interactive visualisations can often be faster.
Although you can do some data wrangling and modelling within Observable, R arguably has much better capabilities for statistical modelling. If you want the best of both worlds, it might make sense to use for wrangling and modelling, then use Observable for creating interactive, web-based visualisations. Luckily, there’s an easy way to do just that using Quarto. This blog post will walk through the process of performing some data wrangling in R, passing the data to Observable, and creating a visualisation using Observable.
Quarto is an open-source scientific and technical publishing system, that allows you to easily combine code with narrative text to create reproducible outputs. It works with code written in Python, R, Julia, or Observable. If you haven’t used Quarto before, I’d recommend checking the Get Started section of the documentation. You can also view my Introduction to Quarto training course materials.
If you’re someone who is intrigued by JavaScript libraries like D3.js for creating visualisations, but are discouraged by how complicated it looks and how steep the learning curve might be, then using Observable with Quarto can be a gentler introduction. You also don’t need to install or set up any additional software. As long as you have Quarto installed, your document will render in such a way that enables the use of Observable within your outputs.
Data wrangling in R
Let’s dive into an example! We start by creating a new Quarto document. You can choose to leave the YAML empty if you want, but I recommend at least hiding the code by setting echo: false
to make sure only your visualisations end up in the final output.
|
|
We’ll be using data on the history of Himalayan Mountaineering Expeditions, which was used as a
TidyTuesday dataset in January 2025. We can load the data using the {tidytuesdayR}
package before performing any data wrangling in R as we normally would:
|
|
We’ll focus on the peaks_tidy
data here where the first few lines look like:
|
|
Here, we’ll keep it reasonably simple and look at the relationship between the first year a climb was recorded (PYEAR
) and the height of the peak (HEIGHTM
). We’ll also look at the Himalayan region that each peak is in (REGION_FACTOR
). We can filter the data and select the columns we’re interested in using {dplyr}
(or base R if you prefer):
|
|
Our data now looks like this:
|
|
It’s reasonably tidy, and we’re ready to start plotting (in Observable).
Observable code blocks
In Quarto, an Observable code block is added in a very similar way to R code blocks. Instead of specifying the language using {r}
, we use {ojs}
instead. Here, ojs
stands for Observable JavaScript (OJS), although it’s important that {ojs}
is in lowercase:
|
|
Unlike R, Observable code blocks don’t need to be in order. This means that you can group all of your output code together, and all of your data processing code together to keep your document looking cleaner (similar to the separation of ui
and server
in Shiny apps).
Passing data from R to Observable
To pass our clean, wrangled data from R to Observable, we use the ojs_define()
function, where we define what we want the object to be called in Observable (r_data
) and what object we want to pass from R (plot_data
):
|
|
Note that this is an R code block, not an Observable code block. The Observable object name doesn’t need to be r_data
, and can be the same name as the R object (or almost anything else)!
R passes the data to Observable in a by column format. Depending on which functions or libraries in Observable you use to visualise your data, it might need to be passed in a by row format instead. In Observable, we can use the transpose()
function to switch from by column to by row format - since the basic Plot()
function we’ll be using later requires it in this format:
|
|
An alternative approach could be to save the wrangled data to a CSV or JSON file in R, and read it into Observable as a local file using the FileAttachment()
function. See the
data sources section of the Quarto documentation for different ways to read in data.
Using other libraries
When using core libraries that come with Observable (such as Observable Plot
), there’s no need to install or load anything additional. However, if you want to use non-core libraries like D3 or
Arquero (a library inspired by {dplyr} for data transformation), it’s fairly straightforward to do it. We simply need to be explicit about importing those libraries. For example, to access functions from D3, we would run:
|
|
For this example, we’ll be able to do everything use the core Observable Plot library, so we don’t need to use any additional libraries.
Plotting with Observable
Observable Plot is a JavaScript library, primarily aimed at creating exploratory data visualisations, which is one of the core Observable libraries. You can draw many of the most common types of charts using the plot()
function from the Plot
library. Within Plot.plot()
, we specify marks
to define the geometries that are drawn such as lines or dots; color
to define the colours that are used; as well as other arguments like title
and subtitle
which can use to add text. If you’re a {ggplot2}
user, you might notice some similarities since it also follows a Grammar of Graphics approach.
Again, we’ll keep it simple for this introductory example and create a basic scatter plot of year against peak height for our Himalayan Expeditions data. In the marks
argument, we start by passing in the function for the geometry we want to draw. To create a scatter plot, we need to draw dots, so we use the Plot.dot()
function. The first argument is the data set we’re plotting, and the second argument is where we specify which columns of the data map to which axis. Again, if you’re a {ggplot2}
user, this is very similar to setting the arguments for the data and aesthetic mapping using aes()
.
|
|
This gives us a very basic scatter plot, and there are a few adjustments that we might want to make to improve it’s clarity. You might notice that the x-axis looks a little bit odd for data representing years. The year values are treated as numbers, and so Observable formats them with commas. This is helpful when we are plotting large numbers, but not so helpful when they are actually years. There are a couple of approaches we could take, but the easiest way is to convert the PYEAR
column from a number to a date, and Observable will know how to format it correctly:
|
|
Let’s also edit the axis labels to something more readable than the column names, and add a grid in the background. We use the grid
, x
, and y
arguments to make these adjustments, remembering to also update the data to our new (correctly typed) dataset:
We can add comments to document our code using
//
in the same way we use#
in R.
|
|
To change the colour of the points, we edit the mapping to also set the fill
colour of the points to be based on the values in the REGION_FACTOR
column. Within the color
argument, we can set whether or not we want a legend, and also choose a colour palette in the scheme
argument.
Observable colour palettes: The Observable documentation has an interactive colour palette viewer, where you can browse different sequential, diverging, and discrete colour palettes. The built-in options include the ColorBrewer palettes, which are also available in R.
|
|
Finally, we can add a title, subtitle, and caption to the plot. We can also set the size of the plot area, and change the sizes of the margins to add a little bit more space around the plot:
|
|
This introductory example showcases only a very small amount of what you can do with Observable. If you start making more plots in Observable, you’ll likely notice that there are lots of different ways to do things. For example, you could edit the axis labels using the Plot.AxisX()
function instead of in the x
argument. You could also use another library entirely (such as D3) to create a scatter plot. And since Observable is JavaScript-based, you can also use CSS to edit the styling of plot elements, or add hover effects. In terms of interactivity, you can easily add dropdown menus or sliders to plot different subsets of your data, using the Observable Inputs
(core) library.
One of the other nice features of Observable is that, if you see a complicated plot that you like, it’s fairly straightforward to import the notebook that creates it and replace the data with your own. See quarto.org/docs/interactive/ojs/examples/population for an example of importing a sunburst diagram.
Saving a static image
When you create a plot using Observable in your Quarto document, the images are rendered as SVG . However, you may also want to save a static or raster image file (such as a PNG) to share on social media, for example. Of course, the easiest way might be to simply take a screenshot. But you could automate it using the webshot()
function from the {webshot2}
package, and using CSS selectors to capture the Observable cell output with selector = ".cell-output.cell-output-display"
.
Additional resources
The Quarto documentation has lots of information about using Observable in Quarto, including with R and Python. It also has plenty of examples with code.
Deepali Kank has lots of great examples of charts created with Observable and D3, and many with the data wrangling performed in R. This chart about fragrances in perfumes is especially beautiful!
If you’re looking for a different way to work with D3 and R together, then the {r2d3} package provides functionality for working with D3 visualisations from R. This Jumping Rivers blog post by Mandy Norrbo provides a nice introduction.
For attribution, please cite this work as:
Observable for R users.
Nicola Rennie. April 1, 2025.
nrennie.rbind.io/blog/observable-r-users
BibLaTeX Citation
@online{rennie2025, author = {Nicola Rennie}, title = {Observable for R users}, date = {2025-04-01}, url = {https://nrennie.rbind.io/blog/observable-r-users} }
Licence: creativecommons.org/licenses/by/4.0