knowledgebase

recipes that save time

View the Project on GitHub hbc/knowledgebase

R Shiny

R Shiny is a package for R that allows users to create interactive HTML pages that can handle a wide range of applications. Due to the diverse array of applications for R Shiny Apps, there is no way to cover all of these in a single page. However, the goal of this page is to provide a primer on how to start building R Shiny Apps.

Learning Objectives

Shiny Structure

When creating an app within R Shiny the first place to start is by naming your app. It is a good idea to name your R script app.R and place it within a directory that has a meaningful name. For example, your directory might be named Hello_world and within this directory you have the R script, app.R. This convention will be helpful later when you try to host your app online somewhere.

Next, we need to understand the difference between the user interface (UI) and server. The UI is the HTML rendering of your Shiny App and is referred to as your front-end since it is what users who interact with your app will see. The server is where the inputs from the UI are processed and output to the UI is returned from. The server is oftentimes referred to as the back-end.

The last component of the a Shiny app is the shinyApp function which launches the app.

Shiny Syntax

A typical Shiny App will have a structure that at bare minimum looks something like:

ui <- fluidPage(
    [format]Input("input_variable"),
    ...
    [format]Output("output_variable")
    )
    
server <- function(input, output){
    output$output_variable <- render[Format]({
        input$input_variable ...
    })
}

shinyApp(ui = ui, server = server)

Let’s break this down a bit more and talk about each of these components:

First, let’s talk about the UI side where everything gets assigned to the ui object:

Second, let’s talk about the server side where everything is assigned to the server object:

Lastly, the UI and the server side are tied together and the app is launched with:

shinyApp(ui = ui, server = server)

Your First R Shiny App

Let’s write a simple app that can help reinforce some of these principles. The goal of this app is simply to return text that you have provided as input. While this is an overly simplistic app, it will demonstrate many of the core ideas that are integral to an R Shiny App. Here is the code we will use:

library(shiny)

ui <- fluidPage(
    textInput("input_text", "My input text"),
    textOutput("output_text")
)

server <- function(input, output){
    output$output_text <- renderText({
        input$input_text
    })
}

shinyApp(ui = ui, server = server)

Once we have copied and pasted this, we can hit Command + Shift + Return on a Mac or hit “Run App” in the top of your R Studio window. Your app should pop up looking like:

And when you type something in like “Hello World”, “Hello World” should appear below:

NOTE: If you look at your console it is currently listening for input and you cannot use your console for other tasks. Before you can do anything else in R, you will need to close the app or hit the stop sign in the console.

Let’s discuss what is happening here:

NOTE: The order of inputText() and outputText() will determine where they are placed on our HTML page. If we had outputText() before inputText(), then our returned text would be above our input text on the rendered HTML page.

The process of this app is outlined in the figure below:

While, we have written a simplistic app here, it has many of the core features that we are interested in utilizing. Armed with this basic understanding of some of the R Shiny syntax, let’s tackle a slightly more complex R Shiny App.

Your Second R Shiny App: Using a slider to provide input and then performing an arithmetic function on it

In this app we are going to use a slider to select a value and then have our app return the squared value of it. The code for this app should look like:

library(shiny)

ui <- fluidPage(
    sliderInput("number", "Select a number to square", min = 1, max = 10, value = 5),
    textOutput("squared_number")
)

server <- function(input, output){
    output$squared_number <- renderText({
        input$number ** 2
    })
}

shinyApp(ui = ui, server = server)

Run the app and the pop-up window should look like:

Let’s take a closer look at the new parts to this code:

Hopefully, at this point you are becoming slightly more comfortable with the syntax employed by R Shiny! Let’s try another example to further our understanding.

Exercise

Create an R Shiny App that does the following:

1) Collects a numeric input 2) Collects input from a slider ranging from 1 to 20 3) Multiples the numeric input from 1) by the the slider numeric input from 2) 4) Returns the product as text

Click here to see the answer
library(shiny)
ui <- fluidPage(   numericInput("a", "First number to multiply", 5),   sliderInput("b", "Second number to multiply", min = 1, max = 20, value = 5),   textOutput("product") ) server <- function(input, output){   output$product <- renderText({     input$a * input$b   }) } shinyApp(ui = ui, server = server)

Your Third R Shiny App: Selecting a value from a dropdown menu for sample size and creating a random normal distribution from this sample size

In this app, we are going to select a value to use as our sample size from a dropdown menu and then have the app return a random normal distribution plot using this value as the sample size parameter. Copy and paste the code below:

library(shiny)

ui <- fluidPage(
    selectInput("normal_sample_size", "What should be the sample size?", c(5, 25, 50, 100)),
    plotOutput("normal_dist")
)

server <- function(input, output){
  output$normal_dist <- renderPlot({
    normal_random_numbers <- rnorm(input$normal_sample_size)
    hist(normal_random_numbers)
  })
}

shinyApp(ui = ui, server = server)

When we run this app is could look like:

Now we can click on the dropdown menu and select a new sample size and the histogram will update. If we were to select 100, then it could look like:

We have added a bit more complexity to this app, so let’s take a look at it:

NOTE: You can also use the multiple = TRUE option within selectInput() if you would like to be able to select multiple values. For this context it isn’t appropriate, but in other contexts it could be.

Your Fourth R Shiny App: Using radio buttons to select a data table to render

In this app, we are going to select between two possible built-in R data tables for Shiny to render. Importantly, you should use the DT package when rendering data tables with R Shiny. The DT package is the recommended package from the Shiny documentation to use when rendering tables.

library(shiny)
library(DT)

ui <- fluidPage(
  radioButtons("dataset", "Select dataset", c("iris", "mtcars")),
  DTOutput("table")
)

server <- function(input, output){
  output$table <- renderDT({
    if (input$dataset == "iris"){
      iris
    }
    else if (input$dataset == "mtcars"){
      mtcars
    }
  })
}

shinyApp(ui = ui, server = server)

When we run the app, it should look like:

Additionally, we can toggle the dataset we want and it will update in real-time and if we switched to the mtcars dataset it would look like:

Let’s talk about how this app works:

Your Fifth R Shiny App: Using checkboxes to select input and an action button to control reactivity

In this app we will introduce checkboxes to select inputs and use an action button to control the reactivity of the app. Previous versions of R Shiny used the eventReactive() function to control actions, such as clicking of an action button. However, in Shiny 1.6 this has changed to using bindEvent(), so we will demonstrate it here.

library(shiny)

ui <- fluidPage(
  checkboxGroupInput("values", "Select numbers to sum", c(1:10)),
  actionButton("calculate", "Calculate!"),
  textOutput("sum")
)

server <- function(input, output){
  addition <- reactive(
      sum(as.numeric(input$values)) 
  ) %>% 
  bindEvent(input$calculate)
  output$sum <- renderText({addition()})
}

shinyApp(ui = ui, server = server)

This app should look like:

We can break apart this

Modfying the Action button

We can customize our action button a bit using the class option within actionButton():

library(shiny)

ui <- fluidPage(
  checkboxGroupInput("values", "Select numbers to sum", c(1:10)),
  actionButton("calculate", "Calculate!", class = "btn-success"),
  textOutput("sum")
)

server <- function(input, output){
  addition <- reactive(
      sum(as.numeric(input$values)) 
  ) %>% 
  bindEvent(input$calculate)
  output$sum <- renderText({addition()})
}

shinyApp(ui = ui, server = server)

Now the app should look like:

To get the green action button, we just needed to add class = "btn-success" toactionButton().

Other values of class that might be of interest are:

You can also combine the color and size options for class by using class = "btn-success btn-lg", which will create a larger, green button.

Creating a side panel on our Shiny App

Now we are going to start formatting our UI a bit. In order to minimize the number of moving parts, let’s go back to using our third app to do this. Copy and paste the following app:

library(shiny)

ui <- fluidPage(
  sidebarPanel(
    selectInput("normal_sample_size", "What should be the sample size?", c(5, 25, 50, 100))
  ),
  mainPanel(
    plotOutput("normal_dist")
  )
)

server <- function(input, output){
  output$normal_dist <- renderPlot({
    normal_random_numbers <- rnorm(input$normal_sample_size)
    hist(normal_random_numbers)
  })
}

shinyApp(ui = ui, server = server)

This app should now look like:

You may need to widen the window or view it in a browser in order for it to render with the side panel on the the left side.

A few edits we’ve made to the UI to create this involves:

Let’s add some tabs to the top of our app

In the next app we are going to add tabs along the top of our app that allows us different distributions to look at:

library(shiny)

ui <- fluidPage(
  navbarPage("Distributions",
    tabPanel("Normal",
      sidebarPanel(
        selectInput("normal_sample_size", "What should be the sample size?", c(5, 25, 50, 100))
      ),
      mainPanel(
        plotOutput("normal_dist")
      )
    ),
    tabPanel("Uniform",
      sidebarPanel(
        selectInput("uniform_sample_size", "What should be the sample size?", c(5, 25, 50, 100))
      ),
      mainPanel(
        plotOutput("uniform_dist")
      )
    )
  )
)

server <- function(input, output){
  output$normal_dist <- renderPlot({
    normal_random_numbers <- rnorm(input$normal_sample_size)
    hist(normal_random_numbers)
  })
  output$uniform_dist <- renderPlot({
    uniform_random_numbers <- runif(input$uniform_sample_size)
    hist(uniform_random_numbers)
  })
}

shinyApp(ui = ui, server = server)

This app should look like:

Below we explain the functions we added to this app:

We have added symmetrical code for a uniform distribution on the UI-side and server-side.

Adding a theme to our Shiny App

Shiny has several themes to choose from but we first need to load the shinythemes library to have them availible to us. We are going to go ahead and add the theme “cerulean” to our app, but you can pick any of the themes:

library(shiny)
library(shinythemes)

ui <- fluidPage(theme = shinytheme("cerulean"),
  navbarPage("Distributions",
    tabPanel("Normal",
      sidebarPanel(
        selectInput("normal_sample_size", "What should be the sample size?", c(5, 25, 50, 100))
      ),
      mainPanel(
        plotOutput("normal_dist")
      )
    ),
    tabPanel("Uniform",
      sidebarPanel(
        selectInput("uniform_sample_size", "What should be the sample size?", c(5, 25, 50, 100))
      ),
      mainPanel(
        plotOutput("uniform_dist")
      )
    )
  )
)

server <- function(input, output){
  output$normal_dist <- renderPlot({
    normal_random_numbers <- rnorm(input$normal_sample_size)
    hist(normal_random_numbers)
  })
  output$uniform_dist <- renderPlot({
    uniform_random_numbers <- runif(input$uniform_sample_size)
    hist(uniform_random_numbers)
  })
}

shinyApp(ui = ui, server = server)

The app should now look like:

The only alteration to our code to add a theme was adding this line after opening fluidPage():

Launching directly into browser

If you would like to see the app visualized in a browser, you have two choices:

1) From the pop-up window you can left-click the “Open in Browser” button. Be aware that if you close the browser window with the app, you will still need to hit the stop sign above in the Console to stop the app or close the pop-up.

2) Alternatively, you can modify the shinyApp command to include options = list(launch.browser = TRUE) and it will automatically launch the app in your local browser. The full command would look like:

shinyApp(ui = ui, server = server, options = list(launch.browser = TRUE))

In this scenario, you would still need to hit the stop sign button in the console to stop the app from running, even after you’ve closed the browser window.

Making Required Inputs

Consider the scenario where you want R Shiny to only evaluate some logic after some input has been provided. You could use an action button to accomplish this task as we’ve discussed before. However, you can also use the req() function. The ‘req()’ function checks for required values before an evaluation of logic is done. Let’s compare two examples to illustrate this:

library(shiny)
library(tidyverse)

ui <- fluidPage(
  textInput("input_text", "What is the sum of 2 + 2?"),
  textOutput("output_text"),
)

server <- function(input, output) {
  output$output_text <- renderText({
    if (input$input_text == "4"){
      "Correct!"
    }
    else ("Wrong!")
  })
}

# Run the application 
shinyApp(ui = ui, server = server)

In this case, the word “Wrong!” appears by default because R Shiny evaluates the initial state of the input as blank and states that this is not equal to 4, so it returns “Wrong!”. However, this is not what we want because the user hasn’t even tried to to provide input yet, so stating that they are wrong prematurely seems incorrect. Thus, we will add a req() function to require that in the input_text actually exists:

library(shiny)
library(tidyverse)

ui <- fluidPage(
  textInput("input_text", "What is the sum of 2 + 2?"),
  textOutput("output_text"),
)

server <- function(input, output) {
  output$output_text <- renderText({
   req(input$input_text, cancelOutput = TRUE)
    if (input$input_text == "4"){
      "Correct!"
    }
    else ("Wrong!")
  })
}

# Run the application 
shinyApp(ui = ui, server = server)

Now we can see that R Shiny only returns “Wrong!” or “Correct!” after we have provide the input that is required. However, you can see that once we have initally defined this variable and then deleted the text, the req() condition is still being meet. Oftentimes, you would not want to use the req() function in this way, but rather that an initial parameter, like a file being loaded (which we will explore in the next section), has been met.

Loading External Files

Hosting Options

One great aspect of Shiny Apps is that you can host them on internet for anyone to use. However, you will need a server that allows you to host R Shiny apps.

HMS

Currently, HMS is carrying out a pilot program for hosting R Shiny apps here called the Research Data Visualization Platform.

Shinyapps.io

Another option that allows you to do this is shinyapps.io. You will need to make an account in order to host your apps here. A free account lets you host apps with low usage and limits the number of apps you can host. Of course, you can pay if you’d like to allow higher usage and more apps. Once you have your app written in an app.R Rscript within a directory, you can configure your account and deploy it using these instructions.

Additional Resources

Mastering Shiny


Back to Knowledgebase