Introduction to robservable

Julien Barnier, Kenton Russell

2020-09-28

robservable allows the use of Observable notebooks (or part of them) as htmlwidgets in R.

Note that it is not just an iframe embedding a whole notebook. Cells are integrated directly into the document or application with no iframe wrapper. You can choose what cells to display, update cell values from R, and add observers to cells to get their values back into a Shiny application.

Embed a whole notebook

The most basic usage is to call the robservable() function with the full URL or the identifier of the notebook you want to display. The identifier is the same as the one used to import another notebook in Observable (something like @d3/horizontal-bar-chart). Please see the Introduction to Imports notebook for additional reference.

For example, the two following commands are equivalent and should display the whole corresponding notebook1 :

robservable("https://observablehq.com/@d3/horizontal-bar-chart")
robservable("@d3/horizontal-bar-chart")

If the notebook is shared but not published, you can use the full URL or the hash identifier.

robservable("https://observablehq.com/d/61be71d2b8c0d456")
robservable("61be71d2b8c0d456")

Choose which cells to render

Instead of rendering a whole notebook, we can choose to display only some of its cells. This is done by passing a character vector of cell names to the include argument.

robservable(
  "@d3/horizontal-bar-chart", 
  include = "chart"
)

If you need to display an unnamed cell2, you can do it by specifying its number, ie its position in the notebook (starting with 1). For example, to display the first cell of a notebook if it is unnamed you would use include = 1.

robservable(
  "@d3/horizontal-bar-chart", 
  include = 1
)

Note that specifying a named cell by its position will not display it. You have to add its name to include.

For some notebooks you’ll have to render several cells to get the desired result. For example, in the eyes notebook, the main chart is in a cell named canvas, but it doesn’t render anything if mouse value is not present. For the chart to be created, you have to render both cells.

robservable(
  "@mbostock/eyes",
  include = c("canvas", "mouse")
)

In this case, we may want to render mouse without displaying it. This is possible by adding its name to the hide argument.

robservable(
  "@mbostock/eyes",
  include = c("canvas", "mouse"),
  hide = "mouse"
)

Finally, it is possible to mix the use of named and unnamed cells both in cell and hide, so you can do something like below.

robservable(
  "@mbostock/eyes",
  cell = c(1, "canvas", "mouse"),
  hide = "mouse"
)

Update cell values

robservable allows to update a notebook cell value directly from R. This is done by passing a named list as the input argument.

For example, in the horizontal bar chart notebook there is a cell called barHeight which allows to customize the bar heights. We can modify its value when calling robservable with input = list(barHeight = 15).

robservable(
  "@d3/horizontal-bar-chart", 
  include = "chart",
  input = list(barHeight = 15)
)

More interesting, we can update the data cell value of the notebook to generate the bar chart based on our own data. We just have to be sure that it is in the same format as the notebook data. In this example the data is in a standard d3-array format, so we can pass a data frame. We will need to take care of the column names, as they must match the ones in the notebook data.

df <- data.frame(table(iris$Species))
# change column names to match the names used in the observable notebook
names(df) <- c("name", "value")

robservable(
  "@d3/horizontal-bar-chart", 
  include = "chart",
  input = list(data = df)
)

There’s still one problem though. Our species names are truncated. We can fix this because the notebook allows us to change the margins of the plot by modifiying the margin cell. As this cell value is a JavaScript object, we can update it by passing a named list.

robservable(
  "@d3/horizontal-bar-chart",
  include = "chart",
  input = list(
    data = df,
    # equivalent to {top: 20, right: 0, left: 70, bottom: 0} in JavaScript
    margin = list(top = 20, right = 0, left = 70, bottom = 0)
  )
)

Finally, here is a bit more complex example which displays a multi-line chart with the gapminder data. The to_js_date function is a helper to convert Date or POSIXt R objects to JavaScript Date values (ie number of milliseconds since Unix Epoch).

library(gapminder)
data(gapminder)
series <- lapply(unique(gapminder$country), function(country) {
  values <- gapminder[gapminder$country == country, "lifeExp", drop = TRUE]
  list(name = country, values = values)
})
dates <- sort(unique(gapminder$year))
dates <- as.Date(as.character(dates), format = "%Y")

df <- list(
  y = "Life expectancy",
  series = series,
  dates = to_js_date(dates)
)

robservable(
  "@d3/multi-line-chart",
  include = "chart",
  input = list(data = df)
)

Size the widget

Widget sizing is a bit complicated as it depends on several factors :

By default, robservable overrides the potential width and height notebook values by the htmlwidget container HTML element width and height. This override is performed both at widget creation and on widget resizing. Overriding width allows making the widget “fit” in its container, and avoids updating size when the page is resized but not its container (which is the case when width is taken from Observable standard library). Overriding height has the same purpose, but not all notebooks define a height value, so unlike width, height won’t always have an effect.

This value override allows the following figure to fit in the widget dimensions.

robservable(
  "@mbostock/eyes",
  include = c("canvas", "mouse"),
  hide = "mouse"
)

If you explicitly specify the width and height of the widget with the corresponding arguments, the cell values will be updated accordingly.

robservable(
  "@mbostock/eyes",
  include = c("canvas", "mouse"),
  hide = "mouse",
  width = 500,
  height = 100
)

If the notebook doesn’t provide a height value, then you’ll have to manually define an height suitable for the output.

robservable(
  "@d3/bivariate-choropleth",
  include = "chart",
  height = 450
)

Finally, if you provide both a widget height and an height value with the input argument, the second one is not overriden.

robservable(
  "@mbostock/eyes",
  include = c("canvas", "mouse"),
  hide = "mouse",
  input = list(height = 50),
  height = 200
)

There are some cases when these width and height overrides are not suitable. First, those values could be defined for something else than an output size (height could be another type of parameter). Second, overriding the height can modify the chart appearance. In these cases, you can set the update_width or update_height arguments to FALSE to deactivate the value override.

For example, on our horizontal bar chart, it changes the original bar heights. To avoid this effect, we can deactivate height override with the update_height = FALSE argument.

df <- data.frame(table(iris$Species))
names(df) <- c("name", "value")

robservable(
  "@d3/horizontal-bar-chart",
  include  = "chart",
  input = list(
    data = df,
    margin = list(top = 20, right = 0, left = 70, bottom = 0)
  ),
  update_height = FALSE
)

Write notebooks for robservable

If you are developing Observable notebooks to be used with robservable, here is some advice to make your notebook easier to use in R:


  1. The result is not displayed here as a whole notebook is not very suitable for embedding in a document

  2. Another option for unnamed cells is to fork the notebook in Observable and to add a name yourself.