The interactive web app development in R requires multiple relevant skillsets: HTML, CSS, Javascript, R, and Shiny etc. This is a reading note covering the following books:
There are two ways to publish Observable Javascript with Quarto:
Write OJS code in {ojs} cells in the Quarto document (see more details).
Write code using ObservableHQ notebook and share embedded output
Either way has pros and cons: First method can make use of various R packages for data sciences (i.e., tidyverse) if you are R users, it also has seamlessly embedded into .qmd file. However, {ojs} cells do not have autocompletion nor allow you to execute cells without rendering the whole Quarto markdown document. Second method, on the other hand, allows you to write syntax-highlighted OJS code with autocompletion and execute the cells lively, but you have to use Javascript libraries to do data analysis (i.e., Aquero).
A mixing method combining the goods of both methods is performing the data analysis in R, export it to CSV locally or into the cloud (e.g., Google), then using the file attachment function for reading data in the ObservableHQ. Alternatively, if you are not mentally attached to Quarto markdown, the Observable Framework is another option of standalone data visualization project.
Since ObservableHQ is likely long-term maintained, I prefer the second method.
To embed ObservableHQ notebook cells into Quarto, make sure your ObservableHQ notebook is public, then take the following steps in ObservableHQ to have access to a sharable url of one cell:
⋮ > Export > Embed Cells.
Method 2: One example of embedded cell is like the following, note that you may need to upgrade to the Pro version to remove attribution banner.
If you prefer construct the dashboard with javascript locally and use your perfered language (such as R and Python) for data analysis, the Observable Framework offers another option to deploy your javascript+R dashboard. See here for detailed code for “get started of local development”. Following is the toy example I created (Github link).
htmltools::tags$iframe(src="https://jihongz.observablehq.cloud/hello-framework",scrolling ="no", seamless ="seamless",frameborder="0",width="100%", height="1000",title="Hello World Observable Framework")
3 JFR Chapter 3: Introduction to Widgets
The htmlwidgets package originates from the rCharts package (Vaidyanathan, 2025) in 2012 by Ramnath Vaidyanathan. It brought together a plethora of data visualisation JavaScript libraries, datamaps, highcharts, morris.js, and many more. Though no longer maintained rCharts ultimately paved the way towards a framework for interactive visualisations in R: two years later, in 2014, Ramnath and other prominent R users start working on htmlwidgets.
This function puts together the minimalistic structure necessary to implement an htmlwidget and opens play.R, play.js, and play.yaml in the RStudio IDE or the default text editor.
usethis::create_package("playground")htmlwidgets::scaffoldWidget("play")#>Created boilerplate for widget constructor R/play.R#>Created boilerplate for widget dependencies at inst/htmlwidgets/play.yaml#>Created boilerplate for widget javascript bindings at inst/htmlwidgets/play.js
Here’s the directory structure for the project playground:
Then, use document() (cmd+shift+Dcmd+shift+D) and load_all() (cmd+shift+Lcmd+shift+L) to document and load the package.
devtools::document()devtools::load_all()
There is only one function in the playground package, play(), which is the constructor function for the widget. It takes a message as input and returns the message to HTML output.
play(message ="This is a widget!")
Click to see the source code of play.R
#' <Add Title>#'#' <Add Description>#'#' @import htmlwidgets#'#' @exportplay <-function(message, width =NULL, height =NULL, elementId =NULL) {# forward options using x x =list(message = message )# create widget htmlwidgets::createWidget(name ='play', x,width = width,height = height,package ='playground',elementId = elementId )}#' Shiny bindings for play#'#' Output and render functions for using play within Shiny#' applications and interactive Rmd documents.#'#' @param outputId output variable to read from#' @param width,height Must be a valid CSS unit (like \code{'100\%'},#' \code{'400px'}, \code{'auto'}) or a number, which will be coerced to a#' string and have \code{'px'} appended.#' @param expr An expression that generates a play#' @param env The environment in which to evaluate \code{expr}.#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This#' is useful if you want to save an expression in a variable.#'#' @name play-shiny#'#' @exportplayOutput <-function(outputId, width ='100%', height ='400px'){ htmlwidgets::shinyWidgetOutput(outputId, 'play', width, height, package ='playground')}#' @rdname play-shiny#' @exportrenderPlay <-function(expr, env =parent.frame(), quoted =FALSE) {if (!quoted) { expr <-substitute(expr) } # force quoted htmlwidgets::shinyRenderWidget(expr, playOutput, env, quoted =TRUE)}