Adding interactivity to a data report is a highly effective way of communicating information and enabling users to explore a data set. This chapter describes the Shiny1 framework for building interactive applications using R
. This will allow you to create dynamic systems in which users can choose what information they want to see, and how they want to see it.
1Shiny: http://shiny.rstudio.com
Shiny provides a structure for communicating between a user interface (i.e., a web browser) and a data server (i.e., an R
session), allowing users to interactively change the “code” that is run and the data that are output. This not only enables developers to create interactive data presentations, but provides a way for users to interact directly with an R
session (without requiring them to write any code).
Shiny is a web application framework for R
. As opposed to a simple (static) webpage as you would create with R Markdown, a web application is an interactive, dynamic webpage—the user can click on buttons, check boxes, or input text to change how and what data is presented. Shiny is a framework in that it provides the “code” for producing and enabling this interaction, while you as the developer “fill in the blanks” by providing variables or functions that the provided code will use to create the interactive page.
Sharing data with others requires your code to perform two different tasks: it needs to process and analyze information, and then present that information for the user to see. Moreover, with an interactive application, the user is able to interact with the presented data (e.g., click on a button or enter a search term into a form). That user input then needs to be used to re-process the information, and then re-present the output results.
The Shiny framework provides a structure for applications to perform this exchange: it enables you to write R
functions that are able to output (serve) results to a web browser, as well as an interface for showing those outputs in the browser. Users can interact with this interface to send information to the server, which will then output new content for the user. Passing these inputs and outputs back and forth (as illustrated in Figure 19.1) allows Shiny to provide a dynamic and interactive user experience!
R
session and a web browser.Fun Fact
Because Shiny is rendering a user interface for a web browser, it actually generates a website. That is, the framework will create all of the necessary components (HTML elements), their styles (CSS rules), and the scripts (JavaScript code) to enable interactivity. But don’t worry: you don’t need to know anything about these languages; Shiny code is written entirely in R
. However, if you already know a few things about web development, you can augment the Shiny-generated elements and interactivity to really make your application shine.
The Shiny framework involves a number of different components; you will need to be familiar with and distinguish between these terms to understand how to implement Shiny apps.
User interface (UI): The UI of a Shiny app defines how the application is displayed in the browser. The UI provides a webpage that renders R
content such as text or graphics (just like a knitted R Markdown document). Moreover, a Shiny UI supports interactivity through control widgets, which are interactive controls for the application (think: buttons or sliders). The UI can specify a layout for these components, allowing you to organize your content in side-by-side panels, or across multiple tabs.
Server: The server of a Shiny app defines and processes the data that will be displayed by the UI. Generally speaking, a server is a program running on a computer (often remotely) that receives requests and provides (“serves”) content based on the request. For example, when you request information from a web API, you submit a request to a server that processes the request and returns the desired information. In a Shiny application, you can think of the server as an interactive R
session that the user will use to “run” data processing functions by interacting with the UI in the web browser (not in RStudio). The server takes in inputs from the user (based on their interactions) and runs functions that provide outputs (e.g., text or charts) for the UI to display. These data processing functions are reactive, which means they are automatically rerun whenever the input changes (they “react” to it). This allows the output to be dynamic and interactive.
Control widget: An element in the UI that allows the user to provide input to the server—for example, a text input box, a dropdown menu, or a slider. Control widgets store input values, which are automatically updated as the user interacts with the widget. Updates to the value stored by the widget are sent from the UI to the server, which will react to those changes to generate new content to display.
Reactive output: An element in the UI that displays dynamic (changing) content produced by the server—for example, a chart that dynamically updates when the user selects different data to display, or a table that responds to a search query. A reactive output will automatically update whenever the server sends it a new value to display.
Render function: Functions in the server that produce output that can be understood and displayed by the UI’s reactive outputs. A render function will automatically “re-execute” whenever a related control widget changes, producing an updated value that will be read and displayed by a reactive output.
Reactivity: Shiny apps are designed around reactivity: updating some components in the UI (e.g., the control widgets) will cause other components (e.g., the render functions in the server) to “react” to that change and automatically re-execute. This is similar to how equations in a spreadsheet program like Microsoft Excel work: when you change the value in one cell, any others that reference it “react” and change as well.
A Shiny application is written in a script file named app.R
(it must to have that exact name so that RStudio will handle the file correctly). This file should be saved in the root directory of a project (i.e., the root of a git
repository). You can create this file and folder yourself, or alternatively you can create a new Shiny project through the RStudio interface (via File > New File > Shiny Web App…
).
Shiny is made available through the shiny
package—another external package (like dplyr
and ggplot2
) that you will need to install and load before you can use it:
install.packages("shiny") # once per machine library("shiny") # in each relevant script
This will make all of the framework functions and variables you will need to work with available.
Mirroring Figure 19.1, Shiny applications are separated into two components (parts of the application): the UI and the server.
The UI defines how the application is displayed in the browser. The UI for a Shiny application is defined as a value, almost always one returned from calling one of Shiny’s layout functions.
The following example UI defines a fluidPage()
(where the content will “fluidly” flow down the page based on the browser size) that contains three content elements: static text content for the page heading, a text input box where the user can type a name, and the output text of a calculated message
value (which is defined by the server). These functions and their usage are described in more detail in Section 19.2.
# The UI is the result of calling the `fluidPage()` layout function my_ui <- fluidPage( # A static content element: a 2nd level header that displays text h2("Greetings from Shiny"), # A widget: a text input box (save input in the `username` key) textInput(inputId = "username", label = "What is your name?"), # An output element: a text output (for the `message` key) textOutput(outputId = "message") )
The server defines and processes the data that will be displayed by the UI. The server for a Shiny application is defined as a function (in contrast, the UI is a value). This function needs to take in two lists as arguments, conventionally called input
and output
. The values in the input
list are received from the user interface (e.g., web browser), and are used to create content (e.g., calculate information or make graphics). This content is then saved in the output
list so that it can be sent back to the UI to be rendered in the browser. The server uses render functions to assign these values to output
so that the content will automatically be recalculated whenever the input
list changes. For example:
# The server is a function that takes `input` and `output` arguments my_server <- function(input, output) { # Assign a value to the `message` key in the `output` list using # the renderText() method, creating a value the UI can display output$message <- renderText({ # This block is like a function that will automatically rerun # when a referenced `input` value changes # Use the `username` key from `input` to create a value message_str <- paste0("Hello ", input$username, "!") # Return the value to be rendered by the UI message_str }) }
(The specifics of the server and its functions are detailed in Section 19.3.)
The UI and the server are both written in the app.R
file. They are combined by calling the shinyApp()
function, which takes a UI value and a server function as arguments. For example:
# To start running your app, pass the variables defined in previous # code snippets into the `shinyApp()` function shinyApp(ui = my_ui, server = my_server)
Executing the shinyApp()
function will start the app. Alternatively, you can launch a Shiny app using the “Run App” button at the top of RStudio (see Figure 19.2). This will launch a viewer window presenting your app (Figure 19.3); you can also click the “Open in Browser” button at the top to show the app running in your computer’s web browser. Note that if you need to stop the app, you can close the window or click the “Stop Sign” icon that appears on the RStudio console.
Tip
If you change the UI or the server, you generally do not need to stop and start the app. Instead, you can refresh the browser or viewer window, and it will reload with the new UI and server.
When this example application is run, Shiny will combine the UI and server components into a webpage that allows the user to type a name into an input box; the page will then say “Hello” to whatever name is typed in (as shown in Figure 19.3). As the user types into the input box (created by the textInput()
function), the UI sends an updated username
value to the server; this value is stored in the input
argument list as input$username
. The renderText()
function in the server then reacts to the change to the input$username
value, and automatically re-executes to calculate a new renderable value that is stored in output$message
and sent back to the UI (illustrated in Figure 19.4). Through this process, the app provides a dynamic experience in which the user types into a box and sees the message change in response. While this is a simple example, the same structure can be used to create searchable data tables, change the content of interactive graphics, or even specify the parameters of a machine learning model!
Tip
The reactivity involved in Shiny apps can make them difficult to debug. Code statements don’t flow directly from top to bottom as with most scripts, and Shiny may produce somewhat obscure error messages in the console when something goes wrong. As with R Markdown, a good strategy for identifying problematic code is to systematically remove (“comment out”) segments of your project and attempt to rerun your application.
For additional advice on how to fix issues in Shiny apps, see the official Debugging Shiny applicationsa guide.
A Shiny app divides responsibilities between its UI and server: the UI is responsible for presenting information, while the server is responsible for processing information. Enabling such a separation of concerns is a fundamental principle when designing computer programs, as it allows developers to isolate their problem solving and more easily create scalable and collaborative projects. Indeed, this division is the same separation recommended in splitting code across .R
and .Rmd
files.
While it is possible to define both the UI and server in the same app.R
file, you can further emphasize this separation of concerns by instead defining the UI and server in separate files (e.g., my_ui.R
and my_server.R
). You can then use the source()
function to load those variables into the app.R
script for combining. Such a division can help keep your code more organized and understandable, particularly as your apps grow larger.
If you name the separate files exactly ui.R
and server.R
(and have the last value returned in each script be the UI value and the server function, respectively), RStudio will be able to launch your Shiny application without having a unified app.R
file. Even so, it is better practice to use a single app.R
script to run your Shiny app, and then source()
in the UI and server to keep them separated.
Caution
Avoid creating both an app.R
and files named exactly ui.R
and server.R
in your project. This can confuse RStudio and cause your application not to run. Pick one approach or the other!
To enable the rapid discovery of information, you will want to create interfaces that prioritize pertinent information in a clear and organized fashion. The Shiny framework provides structural elements that you can use to construct such a well-organized page.
When you write code defining a UI, you are defining how the app will be displayed in the browser. You create a UI by calling a layout function such as fluidPage()
, which will return a UI definition that can be used by the shinyApp()
function. Layout functions take as arguments the content elements (pieces of content) that you want the layout to contain (and thus will be shown in the app’s UI):
# A "pseudocode" example rendering 3 UI elements in a fluid layout ui <- fluidPage(element1, element2, element3)
A layout function can take as many content elements as needed, each as an additional argument (often placed onto separate lines for readability). For example, the UI shown in Figure 19.2 has three content elements: one produced by the h2()
function, one produced by the textInput()
function, and one produced by the textOutput()
function.
Many different types of content elements can be passed to a layout function, as described in the following sections.
Tip
You can initially implement your app with an “empty” server function as a way to design and test your UI—a UI does not require any actual content in the server! See Section 19.3 for an example of an empty server function.
The simplest type of content element a UI can include is a static content element. These elements specify content that will not change as the user interacts with the page. They are generally used to provide further explanatory information about what the user is looking at—similar to the Markdown portion of an R Markdown document.
Content elements are created by calling specific functions that create them. For example, the h1()
function will create an element that has a first-level heading (similar to using a #
in Markdown). These functions are passed arguments that are the content (usually strings) that should be shown:
# A UI whose layout contains a single static content element ui <- fluidPage( h1("My Static App") )
Static content functions can alternatively be referenced as elements of the tags
list (e.g., tags$h1()
), so they are also known as “tag functions.” This is because static content functions are used to produce HTML,2 the language used to specify the content of webpages (recall that a Shiny app is an interactive webpage). As such, static content functions are all named after HTML tags. But since Markdown is also compiled into HTML tags (as when you knit an R Markdown document), many static content functions correspond to Markdown syntax, such as those described in Table 19.1. See the HTML Tags Glossary3 for more information about the meaning of individual functions and their common arguments.
2HTML tutorials and reference from Mozilla developer network: https://developer.mozilla.org/en-US/docs/Web/HTML
3Shiny HTML Tags Glossary: https://shiny.rstudio.com/articles/tag-glossary.html
Table 19.1 Some example static content functions and their Markdown equivalents
Static Content Function |
Markdown Equivalent |
Description |
---|---|---|
|
|
A first-level heading |
|
|
A second-level heading |
|
|
A paragraph (of plain text) |
|
|
Emphasized (italic) text |
|
|
Strong (bold) text |
|
|
A hyperlink (anchor) |
|
|
An image |
Static content functions can be passed multiple unnamed arguments (i.e., multiple strings), all of which are included as that kind of static content. You can even pass other content elements as arguments to a tag function, allowing you to “nest” formatted content:
# Create a UI using multiple nested content elements ui <- fluidPage( # An `h1()` content element that contains an `em()` content element # This will render like the Markdown content `# My _Awesome_ App` h1("My", em("Awesome"), "App"), # Passing multiple string arguments will cause them to be concatenated (within # the same paragraph) p("My app is really cool.", "It's the coolest thing ever!"), )
It is common practice to include a number of static elements (often with such nesting) to describe your application—similar to how you would include static Markdown content in an R Markdown document. In particular, almost all Shiny apps include a titlePanel()
content element, which provides both a second-level heading (h2()
) element for the page title and specifies the title shown in the tab of a web browser.
Going Further
While your application will include many static elements, the true power and purpose of Shiny come from its support for user interactions. In a Shiny app, users interact with content elements called control widgets. These elements allow users to provide input to the server, and include elements such as text input boxes, dropdown menus, and sliders. Figure 19.5 shows a sample of widgets available in the Shiny package.
Each widget handles user input by storing a value that the user has entered—whether by typing into a box, moving a slider, or clicking a button. When the user interacts with the widget and changes the input, the stored value automatically changes as well. Thus you can almost think of each widget’s value as a “variable” that the user is able to modify by interacting with the web browser. Updates to the value stored by the widget are sent to the server, which will react to those changes to generate new content to display.
Like static content elements, control widgets are created by calling an appropriate function—most of which include the word “input” in the name. For example:
textInput()
creates a box in which the user can enter text. The “Greeting” app described previously includes a textInput()
.
sliderInput()
creates a slider that the user can drag to choose a value (or range of values).
selectInput()
creates a dropdown menu the user can choose from.
checkboxInput()
creates a box the user can check (using checkboxGroupInput()
to group them).
radioButtons()
creates “radio” buttons (the user can select only one of these buttons at a time, just like selecting the station on a radio).
See the documentation4 for a complete list of available control widgets, and the widgets gallery5 for examples.
4Shiny reference: http://shiny.rstudio.com/reference/shiny/latest/
5Shiny Widgets Gallery: http://shiny.rstudio.com/gallery/widget-gallery.html
All widget functions take at least two arguments:
An inputId
(a string) or “name” for the widget’s value. This is the “key” that allows the server to access that widget’s value (literally, it is the key for that value in the input
list argument).
A label
(as a string or static content element) that will be shown alongside the widget and tell the user what the value represents. The label can be an empty string (""
) if you don’t want to show anything.
Other arguments may be required by a particular widget. For example, a slider widget requires a min
, max
, and (starting) value
, as in the code below.
Control widgets are used to solicit input values from the user, which are then sent to the server for processing. See Section 19.3 for details on how to use these input values.
# A UI containing a single slider ui <- fluidPage( sliderInput( inputId = "age", # key this value will be assigned to label = "Age of subjects", # label to display alongside the slider min = 18, # minimum slider value max = 80, # maximum slider value value = 42 # starting value for the slider ) )
To display output values from the server, a UI uses reactive output elements. This kind of content element is similar to a static content element, but instead of displaying unchanging content, it displays dynamic (changing) content produced by the server—for example, a chart that dynamically updates when a user selects different data to display, or a table that responds to a search query. A reactive output will automatically update whenever the server sends it a new value to display.
As with other content elements, reactive outputs are created by calling an appropriate function, most of which include the word “output” in the name. For example:
textOutput()
displays output as plain text; use htmlOutput()
if you want to render HTML content.
tableOutput()
displays output as a data table (similar to kable()
in R
Markdown). Note that the dataTableOutput()
function from the DT
package will display an interactive table.
plotOutput()
displays a graphical plot, such as one created with the ggplot2
package. The plotlyOutput()
function from the plotly
package can be used to render an interactive plot, or you can make a ggplot2
plot interactive.6
6Interactive Plots: http://shiny.rstudio.com/articles/plot-interaction.html
verbatimTextOutput()
displays content as a formatted code block, such as if you wanted to print a non-string variable like a vector or data frame.
Each of these functions takes as an argument the outputId
(a string) or “name” for the value that will be displayed. The function uses this “key” to access the value that is output by the server. For example, you could show the following information generated by your server:
# A UI containing different reactive outputs ui <- fluidPage( textOutput(outputId = "mean_value"), # display text stored in `output$mean_value` tableOutput(outputId = "table_data"), # display table stored in `output$table_data` plotOutput(outputId = "my_chart") # display plot stored in `output$my_chart` )
Note that each function may support additional arguments as well (e.g., to specify the size of a plot). See the documentation for details on individual functions.
Caution
Each page can show a single output value just once (because it needs to be given a unique id
in the generated HTML). For example, you can’t include textOutput(outputId = "mean_value")
twice in the same UI.
Remember
As you build your application’s UI, be careful to keep track of the names (inputId
and outputId
) you give to each control widget and reactive output; you will need these to match with the values referenced by the server!
You can specify how content is organized on the page by using different layout content elements. Layout elements are similar to other content elements, but are used to specify the position of different pieces of content on the page—for example, organizing content into columns or grids, or breaking up a webpage into tabs.
Layout content elements are also created by calling associated functions; see the Shiny documentation or the Layout Guide7 for a complete list. Layout functions all take as arguments a sequence of other content elements (created by calling other functions) that will be shown on the page following the specified layout. For example, the previous examples use a fluidPage()
layout to position content from top to bottom in a way that responds to the size of the browser window.
7Shiny Application Layout Guide: http://shiny.rstudio.com/articles/layout-guide.html
Because layouts themselves are content elements, it’s also possible to pass the result of calling one layout function as an argument to another. This allows you to specify some content that is laid out in “columns," and then have the “columns” be placed into a “row” of a grid. As an example, the commonly used sidebarLayout()
function organizes content into two columns: a “sidebar” (shown in a gray box, often used for control widgets or related content) and a “main” section (often used for reactive outputs such as plots or tables). Thus sidebarLayout()
needs to be passed two arguments: a sidebarPanel()
layout element that contains the content for the sidebar, and a mainPanel()
layout element that contains the content for the main section:
ui <- fluidPage( # lay out the passed content fluidly sidebarLayout( # lay out the passed content into two columns sidebarPanel( # lay out the passed content inside the "sidebar" column p("Sidebar panel content goes here") ), mainPanel( # lay out the passed content inside the "main" column p("Main panel content goes here"), p("Layouts usually include multiple content elements") ) ) )
An example of a sidebar layout is also shown in Figure 19.6.
navbarPage()
and sidebarLayout()
. Red notes are added.
Caution
Because Shiny layouts are usually responsive to web browser size, on a small window (such as the default app viewer) the sidebar may be placed above the content—since there isn’t room for it to fit nicely on the side!
Since a layout and its content elements are often nested (similar to some static content elements), you almost always want to use line breaks and indentation to make that nesting apparent in the code. With large applications or complex layouts, you may need to trace down the page to find the closing parenthesis )
that indicates exactly where a particular layout’s argument list (passed in content) ends.
Because layout functions can quickly become complex (with many other nested content functions), it is also useful to store the returned layouts in variables. These variables can then be passed into higher-level layout functions. The following example specifies multiple “tabs” of content (created using the tabPanel()
layout function), which are then passed into a navbarPage()
layout function to create a page with a “navigation bar” at the top to browse the different tabs. The result is shown in Figure 19.6.
# Define the first page content; uses `tabPanel()` and `sidebarLayout()` # layout functions together (as an example) page_one <- tabPanel( "First Page", # label for the tab in the navbar titlePanel("Page 1"), # show with a displayed title # This content uses a sidebar layout sidebarLayout( sidebarPanel( textInput(inputId = "username", label = "What is your name?") ), mainPanel( h3("Primary Content"), p("Plots, data tables, etc. would go here") ) ) ) # Define content for the second page page_two <- tabPanel( "Second Page" # label for the tab in the navbar # ...more content would go here... ) # Define content for the third page page_three <- tabPanel( "Third Page" # label for the tab in the navbar # ...more content would go here... ) # Pass each page to a multi-page layout (`navbarPage`) ui <- navbarPage( "My Application", # application title page_one, # include the first page content page_two, # include the second page content page_three # include the third page content )
The Shiny framework can be used to develop highly complex layouts just by calling R
functions. For more examples and details on how to achieve particular layout and UI effects, check the Shiny documentation and application gallery.
Fun Fact
Much of Shiny’s styling and layout structure is based on the Bootstrapa web framework, which is how it supports layouts that are responsive to window size. Note that Shiny uses Bootstrap 3, not the more recent Bootstrap 4.
To generate dynamic data views that can be shown in the UI (as reactive outputs), you will need to specify how you want that data to be manipulated based on the user input (through control widgets). In the Shiny framework, you define this manipulation as the application’s server.
You create a Shiny server by defining a function (rather than calling a provided one, as with a UI). The function must be defined to take at least two arguments: a list to hold the input
values, and a list to hold the output
values:
# Define a server function for a Shiny app server <- function(input, output) { # assign values to `output` here }
Note that a server function is just a normal function, albeit one that will be executed to “set up” the application’s reactive data processing. Thus you can include any code statements that would normally go in a function—though that code will be run only once (when the application is first started) unless defined as part of a render function.
When the server function is called to set up the application, it will be passed the input
and output
list arguments. The first argument (input
) will be a list containing any values stored by the control widgets in the UI: each inputId
(“name”) in a control widget will be a key in this list, whose value is the value currently stored by the widget. For example, the textInput()
shown in Figure 19.2 has an inputId
of username
, so would cause the input
list to have a username
key (referenced as input$username
inside of the server function). This allows the server to access any data that the user has input into the UI. Importantly, these lists are reactive, so the values inside of them will automatically change as the user interacts with the UI’s control widgets.
The primary purpose of the server function is to assign new values to the output
list (each with an appropriate key). These values will then be displayed by the reactive outputs defined in the UI. The output
list is assigned values that are produced by render functions, which are able to produce output in a format that can be understood by the UI’s outputs (reactive outputs can’t just display plain strings). As with the UI’s reactive output functions, the server uses different render functions for the different types of output it provides, as shown in Table 19.2.
Table 19.2 Some example render functions and their associated reactive outputs
Render Function (Server) |
Reactive Output (UI) |
Content Type |
---|---|---|
|
|
Unformatted text (character strings) |
|
|
A simple data table |
|
|
An interactive data table (use the |
|
|
A graphical plot (e.g., created with |
|
|
An interactive Plotly plot |
|
|
An interactive Leaflet map |
|
|
Any output produced with |
The result of a render function must be assigned to a key in the output
list argument that matches the outputId
(“name”) specified in the reactive output. For example, if the UI includes textOutput(outputId = "message")
, then the value must be assigned to output$message
. If the keys don’t match, then the UI won’t know what output to display! In addition, the type of render function must match the type of reactive output: you can’t have the server provide a plot to render but have the UI try to output a table for that value! This usually means that the word after “render” in the render function needs to be the same as the word before “Output” in the reactive output function. Note that Shiny server functions will usually have multiple render functions assigning values to the output
list—one for each associated reactive output in the UI.
All render functions take as an argument a reactive expression. A reactive expression is a lot like a function: it is written as a block of code (in braces {}
) that returns the value to be rendered. Indeed, the only difference between writing a function and writing a reactive expression is that you don’t include the keyword function
or a list of arguments—you just include the block (the braces and the code inside it).
# Create a server function that defines a `message` output based on a # `username` input server <- function(input, output) { # Define content to be displayed by the `message` reactive output # `renderText()` is passed a reactive expression output$message <- renderText({ # A render function block can include any code used in a regular function my_greeting <- "Hello " # Because the render function references an `input` value, it will be # automatically rerun when that value changes, producing an updated output message_str <- paste0(my_greeting, input$username, "!") message_str # return the message to be output }) }
Going Further
What is significant about render functions is that they will automatically “rerun” their passed-in code block every time a value they reference in the input
list changes. So if the user interacts with the username
control widget in the UI (and thereby changes the input$username
value), the function in the preceding example will be executed again—producing a new value that will be reassigned to output$message
. And once output$message
changes, any reactive output in the UI (e.g., a textOutput()
) will update to show the latest value. This makes the app interactive!
Remember
In effect, render functions are functions that will be rerun automatically when an input
changes, without you having to call them explicitly! You can think of them as the functions you define for how the output should be determined—and those functions will be rerun when the input changes.
Thus your server defines a series of “functions” (render functions) that specify how the output
should change based on changes to the input
—when that input
changes, the output
changes along with it.
Tip
Data values that are not reactive (that will not change based on user interaction) can be defined elsewhere in the server function, as normal. If you want a nonreactive data value to be available to the UI as well—such as one that contains configuration or static data range information—you should create it outside of the server function in the app.R
file, or in a separate global.R
file. See the Scoping Rules for Shiny Apps articlea for details.
Going Further
ahttps://shiny.rstudio.com/articles/#reactivity
bhttps://shiny.rstudio.com/articles/reactivity-overview.html
chttps://shiny.rstudio.com/articles/understanding-reactivity.html
While the previous sections discussed building and running Shiny apps on your own computer, the entire point of an interactive application is to be able to share it with others. To do that, you will need a website that is able to host the application so that others can navigate to it using their web browsers. However, you can’t just use GitHub Pages to host the application because—in addition to the UI—you need an R
interpreter session to run the server that the UI can connect to (and GitHub does not provide R
interpreters). For this reason, sharing a Shiny app with the world is a bit more involved than simply pushing the code to GitHub.
While there are a few different solutions for hosting Shiny apps, the simplest is hosting through shinyapps.io8. shinyapps.io is a platform provided by RStudio that is used for hosting and running Shiny apps. Anyone can deploy and host five small(ish) applications to the platform for free, though deploying large applications costs money.
8shinyapps.io web hosting for Shiny apps: https://www.shinyapps.io
To host your app on shinyapps.io, you will need to create a free account.9 You can sign up with GitHub (recommended) or a Google account. After you sign up, follow the site’s instructions:
9shinyapps.io signup: https://www.shinyapps.io/admin/#/signup
Select an account name, keeping in mind it will be part of the URL people use to access your application.
Install the required rsconnect
package (it may have been included with your RStudio download).
Set your authorization token (“password”) for uploading your app. To do this, click the green “Copy to Clipboard” button, and then paste that selected command into the Console in RStudio. You should need to do this just once per machine.
Don’t worry about the listed “Step 3 - Deploy”; you should instead publish directly through RStudio!
After you have set up an account, you can publish your application by running your app through RStudio (i.e., by clicking the “Run App” button), and then clicking the “Publish” button in the upper-right corner of the app viewer (see Figure 19.7).
After a minute of processing and uploading, your app should become available online to use at the URL:
# The URL for a Shiny app hosted with shinyapps.io
https://USERNAME.shinyapps.io/APP_NAME/
While it sounds as simple as clicking a button, publishing to shinyapps.io is unfortunately one of the “pain points” in working with Shiny. Things can unexpectedly go wrong, and it’s even more difficult to determine the problem than with local Shiny apps! Here are some useful tips for successfully publishing an application:
Always test and debug your app locally (e.g., on your own computer, by running the app through RStudio). It’s easier to find and fix errors locally; make sure the app works on your machine before you even try to put it online.
You can view the error logs for your deployed app by either using the “Logs” tab in the application view or calling the showLogs()
function (part of the rsconnect
package). These logs will show print()
statements and often list the errors that explain the problem that occurred when deploying your app.
Use correct folder structures and relative paths. All of your app files should reside in a single folder (usually named after the project). Make sure any .csv
or .R
files referenced are inside the app folder, and that you use relative paths to refer to them in your code. Do not ever include any setwd()
statements in your code; only set the working directory through RStudio (because shinyapps.io will have its own working directory).
Make sure that any external packages you use are referenced with the library()
function in your app.R
file. The most common problem we’ve seen involves external packages not being available. See the documentation10 for an example and suggested solutions.
10shinyapps.io build errors on deployment: http://docs.rstudio.com/shinyapps.io/Troubleshooting.html#build-errors-on-deployment
For more options and details, see the shinyapps.io user guide.11
11shinyapps.io user guide: http://docs.rstudio.com/shinyapps.io/index.html
This section demonstrates building a Shiny application to visualize a data set of people who were fatally shot by the police in the United States in the first half of 2018 (January through June). The data set was compiled by the Washington Post,12 which made the data available on GitHub.13 Latitude and longitude have been added to the data based on the city and state; the code for this data preparation is available alongside the full application code in the book code repository.14
12“Fatal Force”, Washington Post: https://www.washingtonpost.com/graphics/2018/national/police-shootings-2018/
13Fatal Shootings GitHub page: https://github.com/washingtonpost/data-police-shootings
14Shiny in Action: https://github.com/programming-for-data-science/in-action/tree/master/shiny
As of the time of writing, there were 506 fatalities captured in the data set during the time period, each one of which has 17 pieces of information about the incident, such as the name, age, and race of the victim (a subset of the data is shown in Figure 19.8). The purpose of the Shiny application is to understand the geographic distribution of where people have been killed by the police, and to provide summary information about the incidents, such as the total number of people killed broken down by race or gender. The final product (shown in Figure 19.10) allows users to select a variable in the data set—such as race
or gender
—through which to analyze the data. This choice will dictate the color encoding in the map as well as the level of aggregation in a summary table.
A main component of this application will be an interactive map displaying the location of each shooting. The color of each point will expresses additional information about that individual (such as race or gender). While the column used to dictate the color will eventually be dynamically selected by the user, you can start by creating a map with the column “hard-coded.” For example, you can use Leaflet (discussed in Section 17.3) to generate a map displaying the location of each shooting with points colored by race of the victim (shown in Figure 19.9):
# Create a map of fatal police shootings using leaflet # Load the leaflet function for mapping library("leaflet") # Load the prepared data shootings <- read.csv("police-shootings.csv", stringsAsFactors = FALSE) # Construct a color palette (scale) based on the `race` column # Using double-bracket notation will make it easier to adapt for use with Shiny palette_fn <- colorFactor(palette = "Dark2", domain = shootings[["race"]]) # Create a map of the shootings using leaflet # The `addCircleMarkers()` function will make circles with radii based on zoom leaflet(data = shootings) %>% addProviderTiles("Stamen.TonerLite") %>% # add Stamen Map Tiles addCircleMarkers( # add markers for each shooting lat = ~lat, lng = ~long, label = ~paste0(name, ", ", age), # add a hover label: victim's name and age color = ~palette_fn(shootings[["race"]]), # color points by race fillOpacity = .7, radius = 4, stroke = FALSE ) %>% addLegend( # include a legend on the plot position = "bottomright", title = "race", pal = palette_fn, # the palette to label values = shootings[["race"]], # again, using double-bracket notation opacity = 1 # legend is opaque )
leaflet
.
Tip
Because server inputs in a Shiny application are strings, it’s helpful to use R
’s double-bracket notation to select data of interest (e.g., df[[input$some_key]]
), rather than relying on dplyr
functions such as select()
.
Tip
A great way to develop a Shiny application is to first build a static version of your content, then swap out static values (variable names) for dynamic ones (information stored in the input
variable). Starting with a working version of your content will make debugging the application much easier.
While this map allows you to get an overall sense of the geographic distribution of the fatalities, supporting it with specific quantitative data—such as the total number of people killed by race—can provide more precise information. Such summary information can be calculated using the dplyr
functions group_by()
and count()
. Note the use of double-bracket notation to pass in the column values directly (rather than referencing the column by name), which will allow you to more easily make the column name dynamic in the Shiny application.
# Calculate the number of fatalities by race # Use double-bracket notation to support dynamic column choice in Shiny table <- shootings %>% group_by(shootings[["race"]]) %>% # pass the column values directly count() %>% arrange(-n) # sort the table in decreasing order by number of fatalities colnames(table) <- c("race", "Number of Victims") # Format column names
With these data representations established, you can begin implementing the Shiny application. For every Shiny app, you will need to create a UI and a server. It’s often useful to start with the UI to help provide structure to the application (and it’s easier to test that it works). To create the UI that will render these elements, you can use a structure similar to that described in Section 19.2.4 and declare a fluidPage()
layout that has a sidebarPanel()
to keep the control widgets (a “dropdown box” that lets the user select which column to analyze) along the side, and a mainPanel()
in which to show the primary content (the leaflet map and the data table):
# Define the UI for the application that renders the map and table my_ui <- fluidPage( # Application title titlePanel("Fatal Police Shootings"), # Sidebar with a selectInput for the variable for analysis sidebarLayout( sidebarPanel( selectInput( inputId = "analysis_var", label = "Level of Analysis", choices = c("gender", "race", "body_camera", "threat_level") ) ), # Display the map and table in the main panel mainPanel( leafletOutput(outputId = "shooting_map"), # reactive output from leaflet tableOutput(outputId = "grouped_table") ) ) )
You can check that the UI looks correct by providing an empty server function and calling the shinyApp()
function to run the application. While the map and data won’t show up (they haven’t been defined), you can at least check the layout of your work.
# A temporarily empty server function server <- function(input, output) { } # Start running the application shinyApp(ui = my_ui, server = server)
Once the UI is complete, you can fill in the server. Since the UI renders two reactive outputs (a leafletOutput()
and a tableOutput()
), your server needs to provide corresponding render functions. These functions can return versions of the “hard-coded” map and data table defined previously, but using information taken from the UI’s input
to select the appropriate column—in other words, replacing the "race"
column with the column named by input$analysis_var
.
Notice the use of input$analysis_var
to dynamically set the color of each point, as well as the aggregation column for the data table.
# Define the server that renders a map and a table my_server <- function(input, output) { # Define a map to render in the UI output$shooting_map <- renderLeaflet({ # Construct a color palette (scale) based on chosen analysis variable palette_fn <- colorFactor( palette = "Dark2", domain = shootings[[input$analysis_var]] ) # Create and return the map leaflet(data = shootings) %>% addProviderTiles("Stamen.TonerLite") %>% # add Stamen Map Tiles addCircleMarkers( # add markers for each shooting lat = ~lat, lng = ~long, label = ~paste0(name, ", ", age), # add a label: name and age color = ~palette_fn(shootings[[input$analysis_var]]), # set color w/ input fillOpacity = .7, radius = 4, stroke = FALSE ) %>% addLegend( # include a legend on the plot "bottomright", title = input$analysis_var, pal = palette_fn, # the palette to label values = shootings[[input$analysis_var]], # again, double-bracket notation opacity = 1 # legend is opaque ) }) # Define a table to render in the UI output$grouped_table <- renderTable({ table <- shootings %>% group_by(shootings[[input$analysis_var]]) %>% count() %>% arrange(-n) colnames(table) <- c(input$analysis_var, "Number of Victims") # format columns table # return the table }) }
As this example shows, in a little less than 80 lines of well-commented code, you can build an interactive application for exploring fatal police shootings. The final application is shown in Figure 19.10, and the full code appears below.
# An interactive exploration of police shootings in 2018 # Data compiled by the Washington Post # Load libraries library(shiny) library(dplyr) library(leaflet) # Load the prepared data shootings <- read.csv("police-shootings.csv", stringsAsFactors = FALSE) # Define UI for application that renders the map and table my_ui <- fluidPage( # Application title titlePanel("Fatal Police Shootings"), # Sidebar with a selectInput for the variable for analysis sidebarLayout( sidebarPanel( selectInput( inputId = "analysis_var", label = "Level of Analysis", choices = c("gender", "race", "body_camera", "threat_level") ) ), # Display the map and table in the main panel mainPanel( leafletOutput("shooting_map"), # reactive output provided by leaflet tableOutput("grouped_table") ) ) ) # Define server that renders a map and a table my_server <- function(input, output) { # Define a map to render in the UI output$shooting_map <- renderLeaflet({ # Construct a color palette (scale) based on chosen analysis variable palette_fn <- colorFactor( palette = "Dark2", domain = shootings[[input$analysis_var]] ) # Create and return the map leaflet(data = shootings) %>% addProviderTiles("Stamen.TonerLite") %>% # add Stamen Map Tiles addCircleMarkers( # add markers for each shooting lat = ~lat, lng = ~long, label = ~paste0(name, ", ", age), # add a label: name and age color = ~palette_fn(shootings[[input$analysis_var]]), # set color w/ input fillOpacity = .7, radius = 4, stroke = FALSE ) %>% addLegend( # include a legend on the plot "bottomright", title = "race", pal = palette_fn, # the palette to label values = shootings[[input$analysis_var]], # double-bracket notation opacity = 1 # legend is opaque ) }) # Define a table to render in the UI output$grouped_table <- renderTable({ table <- shootings %>% group_by(shootings[[input$analysis_var]]) %>% count() %>% arrange(-n) colnames(table) <- c(input$analysis_var, "Number of Victims") # format column names table # return the table }) } # Start running the application shinyApp(ui = my_ui, server = my_server)
By creating interactive user interfaces for exploring your data, you can empower others to discover relationships in the data, regardless of their technical skills. This will help bolster their understanding of your data set and eliminate requests for you to perform different analyses (others can do it themselves!).
Tip
Shiny is a very complex framework and system, so RStudio provides a large number of resources to help you learn to use it. In addition to providing a cheatsheet available through the RStudio menu (Help > Cheatsheets
), RStudio has compiled a detailed and effective set of video and written tutorials.a
For practice building Shiny applications, see the set of accompanying book exercises.15
15Shiny exercises: https://github.com/programming-for-data-science/chapter-19-exercises