Welcome to the Web! Web Development with Shiny 101


  • Classically, websites are built using HTML, CSS, and JavaScript to construct the “client side” of the site, which runs in a user’s browser on their local computer, and by using SQL plus a general programming language like Python to construct the “server side” of the website, which runs remotely on a server that communicates back and forth with the user’s browser to ensure the site is constructed and displayed to the user as intended.
  • R Shiny allows you to build a website using just R + R Shiny code, such that deep understanding of other web development languages like PHP and JavaScript is not required.
  • However, some familiarity with HTML and CSS is practically essential to build a great app and to really understand what you are doing when writing “R Shiny” code.
  • That’s ok—HTML and CSS are very approachable languages compared to general programming languages like R.
  • HTML tells a browser what to put where when building a website. Websites are just “HTML boxes holding stuff and/or other boxes.” Some boxes force new lines after them; others don’t. How boxes will “flow” on wide versus narrow screens is an important design consideration.
  • CSS tells a browser how to display each element on a website. It consists of rules targeting specific HTML boxes that specify new values for those boxes’ aesthetic properties.
  • Website design has matured such that most websites look and feel broadly similar and share many elements, such as buttons, links, widgets, articles, media, and so on. R Shiny lets us add these same elements to our apps.

Building the Ground Floor of a Shiny App


  • Use the three-file system to organize your app’s code to benefit from several R Shiny features within RStudio.
  • A global.R file is handy for storing all the code needed to fuel your app’s start-up and any other code your app needs that only needs to run once.
  • A CSS stylesheet holds CSS code for dictating your app’s aesthetics. One can be linked to an app inside of ui.R using tags$head().
  • UI elements get nested inside one another and must all be placed inside our UI object’s outermost container (here, a fluidPage()).
  • Most UI elements can be given id and class attributes use in CSS selectors. UI elements must be separated from one another in the UI with commas.
  • fluidRow() and column() can be used to create a “grid,” within which elements may arrange next to each other on wide screens but vertically on narrow screens, creating a responsive, mobile-first design with little fuss.
  • CSS styling requires using the right selector to target the right element(s). If you aren’t sure of the right selector to use, you can retrieve it using your browser’s developer tools.

R Shiny's Core Concepts: Rendering and Outputting, Input Widgets, and Reactivity


  • Complex UI elements, like tables, first need to be rendered server-side using a render*({}) function and then placed within our UI using an *Output() function.
  • Rendered entities are passed from the server to the UI via the output object using the outputIds we provided them when we rendered them.
  • Input widgets are UI elements that allow users to interact with our app in pre-defined and familiar ways. The current values of these widgets are passed from the UI to the server via the input object using the inputIds we provided them when we created them.
  • R knows to watch reactive objects (like those attached to input) for changes. Any such changes are events. Event handling is coding how the app should respond to an event.
  • The primary way Shiny handles events is by re-running any reactive contexts containing the reactive object(s) that just changed (unless those objects have been isolate()d, in which case they can’t trigger events but they can be used in operations).
  • Server-side, reactive contexts are triggered to run directively (when a precipitating event occurs), not imperatively (when a coder hits “run”). They might run in any order, many times, or never—it all depends on what the user does.
  • However, once a reactive context begins executing, its contents are executed imperatively, like “normal,” until they complete (even if that takes a long time!), during which time the app will be unresponsive.
  • Observers (like observeEvent({},{})) allow for more precise event-handling; observeEvent({},{})s only rerun their second expression and only in response to events that occur in their first expression, so they enable precise “If X, then Y” event handling.
  • tabPanel() and tabsetPanel() create a “tabular layout,” dividing one UI space into several, only one of which is visible at a time.

Shiny Showstoppers: DTs and Leaflets and Plotlys, Oh My!


  • The DT, leaflet, and plotly packages can be used to produce web-enabled, interactive tables, maps, and graphs for incorporation into Shiny apps.
  • Each of the graphics these packages create come with tons of interactive features. Because some of this functionality might be confusing or unnecessary for some users or contexts, it’s valuable to know how to adjust or disable them. It’s better to have only as many features as are needed and that your users can handle.
  • The aesthetics of these graphics can be adjusted or customized, but the specifics of how to do this (and how easy or intuitive it will be) varies widely.
  • We should strive to update graphics, changing only the aspects that need changing, rather than remaking graphics from scratch. This generally means handling events using observers and proxies rather than using render*({}) functions.
  • Like with other widgets, user interactions with these graphics can be tracked by the UI and passed to the server for handling. This passing happens with the input object for DT and leaflet, but an equivalent system must be used for plotly.