3 Discover Shiny dependencies

3.1 Introduction

If Shiny creates HTML code for us, it is not enough to design a beautiful working app with user interactions. As shown earlier, all modern websites and apps have a lot of CSS and JavaScript under the hood. Let’s explore these elements in the following exercise.

The simplest Shiny layout is the fluidPage(). The shinyapp predefined RStudio snippet creates a basic app skeleton (type shinyapp in a new script or the console in your RStudio IDE, Figure 3.1).

shinyapp code snippet.

FIGURE 3.1: shinyapp code snippet.

We add some text inside and remove the session parameter from the server function, as we don’t need it:

library(shiny)
ui <- fluidPage(
  p("Hello World")
)

server <- function(input, output) {}
shinyApp(ui, server)

At first glance, the page only contains text. Wait … are you sure about this? Let’s run the above example and open the HTML inspector introduced in section 1.3. Results are displayed in Figure 3.2.

Shiny dependencies (with {Shiny} 1.6.0).

FIGURE 3.2: Shiny dependencies (with {Shiny} 1.6.0).

  1. Click on “Open in Browser.”
  2. Open the HTML inspector.
  3. Locate the <head> tag.
  4. Uncollapse it and search for the script containing application/html-dependencies.
  5. List all dependencies.

According to the head section (delimited by <head></head>), Shiny has three main dependencies:

  • jQuery.
  • shiny (custom JavaScript and CSS).
  • Bootstrap (JavaScript and CSS).

As of shiny 1.6.0, json2 is not needed anymore and does not appear in the above list.

3.2 Bootstrap

According to the gitstar ranking, which is an unofficial GitHub star ranking website, Bootstrap comes in at seventh place (144563 stars) and twenty-second place for repositories and organization, respectively. First released in 2011, it welcomed the current and fifth version in May 2021, at the time of writing. It provides plug and play layout and interactive elements, such as tabs, navigation bars, tooltips, popovers and many input (forms), which is rather convenient since it avoids writing a large amount of CSS/JavaScript that would bring more complexity. The first release of Shiny was shipped with Bootstrap 2. Shiny relied a long time on Bootstrap 3 (since v0.11), while many efforts are being made to provide a Bootstrap 4 compatibility and further, essentially through the bslib R package.

One of the great advantage of using Bootstrap is the responsiveness of the design that can work either on desktop or mobile, even though advanced users likely prefer other specialized libraries like Framework7 or onsenUI to develop native-looking apps, as shown in Chapter 22.

Bootstrap 3 relies on the grid layout system, which allows to efficiently organize the content in rows and columns. For instance the fluidRow() and column() Shiny functions leverage the Bootstrap grid to control how elements are displayed on a page.

How does Shiny attach the Bootstrap dependencies? The well-known fluidPage() layout is actually hiding a lower-level function, that is, bootstrapPage(). The latter is also called in many other high-level layout functions, for instance fixedPage(), fillPage() and navbarPage(). It simply attaches bootstraplib, as well as any Bootstrap-compatible theme css files that one can pass with the {shinythemes} package.

As of shiny 1.6.0, end user should use the new theming tools, that is bslib, described in Chapter 9.

The interested reader should have a look at the following code, inside which are defined all the Shiny Bootstrap-based layout elements, such as the well-known fluidPage() wrapper.

3.3 jQuery, DOM manipulation

jQuery allows JavaScript developers to perform DOM manipulation, that is, interacting with HMTL elements, in a more user-friendly manner than with pure JavaScript. At that step, you might not be familiar with JavaScript nor jQuery. Don’t worry! Chapter 10 exposes the basics. Below is a comparison on how you would select a button HTML element with both technologies. We are sure you’ll notice how jQuery2 is more intuitive than JavaScript.

Javascript:

// select the button
var btn = document.getElementById('mybutton');
// event
btn.addEventListener('click', function() {
  alert('You clicked me!'); // action
});

jQuery:

$('#mybutton').on('click', function() {
  alert('You clicked me!');
});

Interestingly, {jquerylib} provides support for shiny by shipping the necessary content. Nowadays, many websites and frameworks (like Bootstrap 5) tend to avoid jQuery so as to gain in performance. Yet, most of Shiny’s JS code still uses it and won’t be removed any-time soon.

3.4 Custom dependencies

The last dependency contains custom JS and CSS code necessary for Shiny to work, that is:

  • Fine-tune the layout.
  • Register input/output and handle every single input/output action. Detailed mechanisms are studied in Chapters 12 and 13.
  • Initialize and control the R/JS communication, as shown in Chapter 11.3.1.
  • Handle alerts/modals/notifications.

Chapters 10.6.3, 12, 15 and 19 showcase the most important elements of those custom files.

3.5 Exercise

In the following exercise, we consider a slightly more complex app with a slider as well as a plot output.

  1. Run the app runExample("01_hello").
  2. Open the HTML inspector.
  3. Delete the bootstrap.min.css and ion.rangeSlider.css dependencies by removing the corresponding DOM element.
  4. What happened to the application’s UI?

To sum up, all of these libraries are necessary to make Shiny what it is! Importantly, Shiny only loads what is necessary to have. For instance, as shown above, it is only necessary to include the slider dependencies when there is at least one slider in the app, which eventually improves app performances.

In Chapter 4, we explore tools to manage HTML dependencies. In Chapters 19.4.1 and 15, we study a special case to insert dependencies at runtime.