# install.packages('pak')
::pak('mjfrigaard/shinypak') pak
29 JavaScript
This chapter will cover how to include JavaScript in your Shiny app-package. Learning a little JavaScript is the quickest way to improve the quality of your Shiny apps (most of the code in a rendered Shiny app is JavaScript). We’ll only cover a few examples here, but I’ve included links for more resources in the callout box below.
R & JavaScript
R and JavaScript have different strengths–R is more accessible for anyone familiar with statistics and math, while JavaScript’s syntax will be more recognizable to those with experience programming in languages like Java and C. JavaScript has unique behaviors (like asynchronous programming and dynamic typing) which can make it challenging, but it’s widely taught due to its essential role in web technologies.
R tends to be on the slower end when compared to other languages, but it works well for stats and it can be sped up with packages (and external languages like C++). JavaScript is fast, especially in web browsers and Node.js, thanks to advanced optimization in modern JavaScript engines like V8.
Both languages have strong communities and ecosystems. R is popular among academics and data professionals 1, while JavaScript has a massive developer community due to its role in web development and supported by many frameworks and libraries.2
R is often developed in IDEs like Posit Workbench or Jupyter Notebooks, while JavaScript is commonly used with text editors such as Visual Studio Code, WebStorm, along with tools like Babel and Webpack.
To recap: R is well suited for statistical analysis and data science, while JavaScript is used for both client and server-side web development, making it useful for creating interactive web pages, as well as mobile and desktop applications.
29.1 JavaScript & htmltools
The first example we’ll use is covered in the Shiny documentation and uses the htmlDependency()
function from the htmltools
package.3 htmltools
is maintained by Posit/RStudio, and it’s one of the underlying workhorses of Shiny, containing “tools for creating, manipulating, and writing HTML from R.”
htmlDependency()
is typically used within a function and included in the standalone app function with the following arguments:
name
The name of JavaScript library.
version
The version of the library or resource we are including.
src
The location of the JavaScript script (in the inst/
folder).
script
The name of the script file (or files).
Alerts
We’ll define a simple JavaScript file below, alert.js
:
document.addEventListener("DOMContentLoaded", function() {
alert("Shiny App with JavaScript!");
; })
Launch app with the shinypak
package:
launch('19.1_js-htmltools')
Below is a createJsDependency()
function with the arguments described above:
#' Create JavaScript Dependency for Shiny App
#'
#' This function creates an `htmltools` dependency object that includes a
#' JavaScript file.
#'
#' @return A dependency object that can be included in a Shiny app's UI.
#'
#' @export
#'
<- function() {
createJsDependency ::htmlDependency(
htmltoolsname = "my-js",
version = "1.0",
src = c(file = system.file("js", package = "sap")),
script = "alert.js"
) }
Note the name
and version
values are arbitrary and are chosen for illustrative purposes. The createJsDependency()
is placed in R/
with the other package functions and alert.js
is placed in inst/js
:
├── R
│ └── createJsDependency.R
└── inst/
└── js/ └── alert.js
We can use createJsDependency()
inside the fluidPage()
function in movies_ui()
:
fluidPage(
theme = shinythemes::shinytheme("spacelab"),
# Include the JavaScript dependency
createJsDependency(),
titlePanel(
After loading, documenting, and building our app-package, we’ll launch to check the new functionality:
Ctrl/Cmd + Shift + L / D / B
library(sap)
launch_app(run = 'b')
This setup encapsulates the dependency definition with the app’s UI definition, ensuring the JavaScript is loaded every time the app is started.
Bookmarking
Our next example has been adapted from the Shiny examples GitHub repo.4 We’re going to use reactR
package to add bookmarking functionality to our Movies app. I’ve placed a small development version of this application in the inst/
folder:
inst/
└──js-app/
├── R/
│ ├── mod_text_input.R
│ ├── mod_text_output.R
│ └── simpleTextInput.R
├── app.R
└── js/
└── input.js
3 directories, 5 files
Launch app with the shinypak
package:
launch('19.2_js-react')
The js-app folder contains two modules (mod_text_input
and mod_text_output
):
js-app modules
# Text Input Module UI ----
<- function(id) {
mod_text_input_ui <- NS(id)
ns $p(simpleTextInput(ns("simpleTextInput")))
tags
}
# Text Input Module Server ----
<- function(id) {
mod_text_input_server moduleServer(id, function(input, output, session) {
return(
reactive(input$simpleTextInput)
)
})
}# Text Output Module UI ----
<- function(id) {
mod_text_output_ui <- NS(id)
ns textOutput(ns("simpleTextOutput"))
}
# Text Output Module Server ----
<- function(id, text) {
mod_text_output_server moduleServer(id, function(input, output, session) {
$simpleTextOutput <- renderText(text())
output
}) }
The modules communicate with a utility function, simpleTextInput()
:
js-app utility function
<- function(inputId, default = "") {
simpleTextInput createReactShinyInput(
inputId = inputId,
class = 'simple-text-input',
dependencies = htmltools::htmlDependency(
'simple-text-input',
'1.0.0',
src = file.path(getwd(), "js"),
script = 'input.js',
all_files = FALSE
),default = default,
container = tags$span
) }
- 1
-
note we aren’t using
system.file()
here because we’re referencing this script locally
simpleTextInput
utilizes the React component by calling createReactShinyInput
. It specifies the input ID, the CSS class, and the dependencies with htmltools::htmlDependency()
(including the path to the input.js
script) to return a React component embedded within Shiny’s UI framework.
The js/
folder contains the input.js
script, which we’ll describe next.
Defining a React Component
Let’s examine how these files work together to create the bookmarking functionality. The input.js
JavaScript file contains a function designed to integrate React components into an R Shiny application. It utilizes the reactR
package, which bridges R and React.js by enabling the use of React components as Shiny inputs.
The JavaScript function SimpleTextInput
in input.js
is defined to create a simple text input field. This functional React component receives props
(properties) from the parent component or from Shiny:
function() {
(function SimpleTextInput(props) {
} }
Create React Element
React Component Structure: The component uses React.createElement
to create an <input>
element. This method takes at least three arguments:
The type of the element (
'input'
in this case).function() { (function SimpleTextInput(props) { return React.createElement('input', { ; }) }
Properties (
props
) of the element, which include:value
is the current value of the input, which is provided byprops.value
.function() { (function SimpleTextInput(props) { return React.createElement('input', { value: props.value, ; }) }
onChange
is a function that updates the state (in Shiny) whenever theinput
changes. This function captures the new value from theinput
field and passes it back to Shiny viaprops.setValue
.function() { (function SimpleTextInput(props) { return React.createElement('input', { value: props.value, onChange: function(e) { .setValue(e.target.value); props }; }) }
Integration with Shiny
The reactR.reactShinyInput
method is called to bind the SimpleTextInput
React component to Shiny. This binding process includes:
A CSS selector (
'.simple-text-input'
) that identifies the HTML element in the Shiny UI to be replaced by this React component..reactShinyInput('.simple-text-input'); reactR
A namespace (
'shiny.examples'
), typically used for managing JavaScript namespaces in larger applications..reactShinyInput('.simple-text-input', 'shiny.examples'); reactR
Passing the
SimpleTextInput
function itself, which defines the behavior and structure of the React component..reactShinyInput('.simple-text-input', 'shiny.examples', SimpleTextInput); reactR; ))()
The final input.js
script in inst/js-app/
is below:
function() {
(function SimpleTextInput(props) {
return React.createElement('input', {
value: props.value,
onChange: function(e) {
.setValue(e.target.value);
props
};
})
}.reactShinyInput('.simple-text-input', 'shiny.examples', SimpleTextInput);
reactR; })()
Usage in app.R
In the R Shiny application (app.R
), we include the bookmarkButton()
in the bslib::sidebar()
with the input module, and place the output module in the bslib::card()
:
js-app app.R file
# packages
library(shiny)
library(reactR)
library(htmltools)
library(bslib)
# app ui
<- function(request) {
ui ::page_fillable(
bslibh1("MoviesApp Bookmark Demo"),
::layout_sidebar(
bslibsidebar =
::sidebar(
bslib$h3("Text Input"),
tagsmod_text_input_ui("txt_in"),
$p(
tagsbookmarkButton()
)
),::card(
bslibfull_screen = TRUE,
::card_header(
bslib$h3("Text Output")
tags
),mod_text_output_ui("txt_out"),
::card_footer(
bslib$em(
tags"Adapted from ",
$a(
tagshref = "https://github.com/rstudio/shiny-examples/tree/main/151-reactr-input",
"Shiny-examples"
)
)
)
)
)
)
}
# app server
<- function(input, output, session) {
server
<- mod_text_input_server("txt_in")
text_val
mod_text_output_server("txt_out", text_val)
}
shinyApp(ui, server, enableBookmarking = "url")
- 1
-
simpleTextInput()
is used withinmod_text_input_ui()
to define a text input element in the ui layout. - 2
-
The text entered into
mod_text_input_ui()
is captured reactively via Shiny throughmod_text_input_server()
… - 3
-
…where it can be used in the server logic and displayed the text output module (
mod_text_output_server()
)
29.2 Recap
R is favored in academic circles and among data professionals, and it has many packages, such as
ggplot2
for data visualization anddplyr
for data manipulation.↩︎JavaScript commands a massive developer community due to its pivotal role in web development, supported by numerous frameworks and libraries like React, Angular, and Vue.js that facilitate web development tasks.↩︎
See the article section titled,
htmlDependency
object↩︎Shiny examples contains over 100 applications demonstrating various features, layout, functionalities, etc.↩︎
The
charpente
package is also covered in Outstanding User Interfaces with Shiny↩︎