General outline for weeks 6 – 9

  • Week 6: Introduction to Shiny and Reactive Programming
  • Week 7: Building Interactive UI Components
  • Week 8: Linking ggplot2 with Shiny for Dynamic Visualisation
  • Week 9: Creating and Deploying Dashboards

Objectives

By the end of this session (and your own reading), you should be able to \(\dots\)

  1. Decide how to choose between different input widgets and how to implement some important ones in Shiny Apps.
  2. Use different layout functions to structure your Shiny App.
  3. Create input widgets dynamically for an interactive user experience.

Download exercises from Week 7 folder on NOW and move them into your R-project directory.

Recap of Shiny Basics

library(shiny)

ui <- fluidPage(
  sliderInput(inputId = "num",
              label = "Choose a number", 
              min = 1, 
              max = 100, 
              value = 50),
  textOutput("result")
)

server <- function(input, output) {
  output$result <- renderText({
    paste("You chose", input$num)
  })
}

shinyApp(ui, server)



Anatomy of Shiny Apps

  • Inputs and outputs
  • Reactivity flow: reactive expressions, observers

Tips

  • Use reactlog::reactlog_enable() to debug
  • Always test small components first
  • Shiny cheatsheet

Short cuts

  • Type shinyapp
  • From menu: File > New File > Shiny Web App...
  • Run: Cmd/Ctrl + Shift + Enter

Input Widgets

Input Widgets

  • Part of user interface:
ui <- fluidPage( ... )
  • Determines how users are allowed to interact with the app.
  • Common inputs:
    • selectInput(): drop-down-menu
    • sliderInput()
    • checkboxInput(): on / off
    • textInput()
    • dateInput()
  • Grouped inputs:
    • radioButtons(): one option
    • checkboxGroupInput(): multiple options
  • Dynamic UI with renderUI() and uiOutput()

Common structure

  • First argument – inputId – is the identifier: connects front end with back end
    • if identifier is “favnum” then server requires input$favnum for access
    • name should only be letters, numbers and underscores and it must be unique
  • Second argument: label a human-readable label for control.
  • Third parameter: value e.g. default value
  • Remaining parameters are unique to the control
sliderInput(inputId = "favnum", label = "Fav number", value = 50, min = 0, max = 100)

Check documentation of input:

?sliderInput

Input Widgets: Free text

What kind of inputs are allowed here?

ui <- fluidPage(
  textInput("name", "What's your name?"),
  passwordInput("password", "What's your password?"),
  textAreaInput("story", "Tell me about yourself", rows = 3)
)

Input Widgets: Numeric inputs

What kind of inputs are allowed here?

ui <- fluidPage(
  numericInput("num", "Number one", value = 0, min = 0, max = 100),
  sliderInput("num2", "Number two", value = 50, min = 0, max = 100),
  sliderInput("rng", "Range", value = c(10, 20), min = 0, max = 100)
)

Input Widgets: Dates

What kind of inputs are allowed here?

ui <- fluidPage(
  dateInput("dob", "When were you born?"),
  dateRangeInput("holiday", "When do you want to go travelling next?")
)

Input Widgets: Limited choices

What kind of inputs are allowed here?

animals <- c("dog", "cat", "mouse", "bird", "other")

ui <- fluidPage(
  selectInput("state", "What's your favourite state?", state.name),
  radioButtons("animal", "What's your favourite animal?", animals), # one selection only
  checkboxGroupInput("animal", "What animals do you like?", animals), # multiple selections allowed
  checkboxInput("restart", "Restart?") # yes / no questions
)

selectInput() can allow multiple selections (multiple = TRUE).

Input Widgets: File uploads

What kind of input is allowed here?

ui <- fluidPage(
  fileInput("upload", NULL)
)

Input Widgets: Action buttons

What kind of input is allowed here?

ui <- fluidPage(
  actionButton("click", "Click me!")
)

Should be paired with observeEvent() or eventReactive() in your server function.

Input Widgets

Function Input Type Description Example Use Case
textInput() Text Single-line text input Enter a name or keyword
textAreaInput() Text Multi-line text input Enter a paragraph or comment
passwordInput() Text (masked) Text input with hidden characters Enter a password
numericInput() Numeric Numeric input with optional min/max/step Set a threshold or value
sliderInput() Numeric (range) Slider for selecting a value or range Choose value or range of values
dateInput() Date Calendar input for selecting a single date Pick a birthdate or deadline
dateRangeInput() Date (range) Select a start and end date Filter data by date range
checkboxInput() Boolean Single checkbox (TRUE/FALSE) Enable or disable a feature
checkboxGroupInput() Categorical Multiple checkboxes for selecting multiple options Choose multiple preferences
radioButtons() Categorical Radio buttons for selecting one option Choose between few options
selectInput() Categorical Dropdown menu for selecting one option Choose between many options
selectizeInput() Categorical Enhanced selectInput() with search/autocomplete Searchable dropdown for long lists
fileInput() File Upload a file from the user’s computer Upload CSV or image file
actionButton() Button Triggers an action when clicked Submit form or refresh plot
actionLink() Button (link style) Like actionButton() but styled as a hyperlink Trigger UI change or navigation
colorInput() Colour Colour picker widget Choose a colour for a plot or theme

Input Widgets

ui <- fluidPage(
  numericInput("num", "Number", 
               value = 0, 
               min = 0, 
               max = 100),
  textOutput("out")
)

server <- function(input, output) {
 output$out <- renderText({
    str_c("This is my number: ", input$num)
 })
}
  • UI: Use input$<id> in the server to access the value of any input widget.
  • Server: render... for reactive behaviour.
  • Server: Combine inputs with observeEvent() or reactive() for dynamic behavior.
  • UI: ...Output to display information.

Exercise: important input options

Review inputs.R

  • When are the different options useful?
  • What happens when you change the input of the value argument?
  • Add reactivity to the server using renderText
  • Print one of the inputs as text using textOutput.

UI Layout Functions

UI Layout Functions

ui <- fluidPage(
  
  
  
  
  
  
  
  
)




Nesting layouts and organising content

  • Static layouts:
    • fluidPage()
    • sidebarLayout()
    • navbarPage()
  • Dynamic layouts:
    • tabsetPanel()
    • conditionalPanel()

UI Layout Functions

ui <- fluidPage(
  sidebarLayout(

    
    
    
    
    
  )
)




Nesting layouts and organising content

  • Static layouts:
    • fluidPage()
    • sidebarLayout()
    • navbarPage()
  • Dynamic layouts:
    • tabsetPanel()
    • conditionalPanel()

UI Layout Functions

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(

    ),
    mainPanel(

    )
  )
)




Nesting layouts and organising content

  • Static layouts:
    • fluidPage()
    • sidebarLayout()
    • navbarPage()
  • Dynamic layouts:
    • tabsetPanel()
    • conditionalPanel()

UI Layout Functions

ui <- navbarPage( # top-level navigation bar.
       
  
  
  
  
  
  
  
  
  
)

Nesting layouts and organising content

  • Static layouts:
    • fluidPage()
    • sidebarLayout()
    • navbarPage()
  • Dynamic layouts:
    • tabsetPanel()
    • conditionalPanel()

UI Layout Functions

ui <- navbarPage( # top-level navigation bar.
        tabPanel( # defines a separate view.
  
          
          
          
          
          
          
          
      )
)

Nesting layouts and organising content

  • Static layouts:
    • fluidPage()
    • sidebarLayout()
    • navbarPage()
  • Dynamic layouts:
    • tabsetPanel()
    • conditionalPanel()

UI Layout Functions

ui <- navbarPage( # top-level navigation bar.
        tabPanel( # defines a separate view.
           sidebarLayout(
            
             
             
             
             
             
          )
      )
)

Nesting layouts and organising content

  • Static layouts:
    • fluidPage()
    • sidebarLayout()
    • navbarPage()
  • Dynamic layouts:
    • tabsetPanel()
    • conditionalPanel()

UI Layout Functions

ui <- navbarPage( # top-level navigation bar.
        tabPanel( # defines a separate view.
           sidebarLayout(
              sidebarPanel(

              ),
              mainPanel (

             )
          )
      )
)

Nesting layouts and organising content

  • Static layouts:
    • fluidPage()
    • sidebarLayout()
    • navbarPage()
  • Dynamic layouts:
    • tabsetPanel()
    • conditionalPanel()

UI Layout Functions

  • fluidPage() is often the top-level layout function in a Shiny app.
  • navbarPage() and tabsetPanel() both use tabs, but:
    • navbarPage() is for top-level navigation (like switching between app sections).
    • tabsetPanel() is for in-page tabbing (like switching between views within a section).
  • conditionalPanel() is the only one that uses JavaScript expressions for dynamic visibility.

UI Layout Functions

Function Purpose Behavior / Structure Special Features
fluidPage() Main layout container Arranges UI elements in a fluid, responsive grid system Adapts to screen size; often the top-level UI function
sidebarLayout() Two-column layout with sidebar and main area Combines sidebarPanel() and mainPanel() for classic dashboard-style layout Good for apps with controls on the side and outputs in the main area
navbarPage() Multi-page navigation with tabs Creates a navigation bar with multiple tabPanel() or navbarMenu() items App-wide navigation
tabsetPanel() Tabbed content within a page Displays one tabPanel() at a time; tabs are shown inline (not in navbar) Can be nested
conditionalPanel() Conditional visibility of UI elements Shows/hides UI elements based on JavaScript condition (e.g., input.x == "yes") Highly dynamic; useful for reactive UI based on user input

Exercise: UI Layout

Review 1_app.R of the week 7 exercises

Task 1: Add a sidebar layout sidebarLayout() (example in 2a_app.R)

  • Sidebar: selectInput() for variable choices
  • Main panel: plotOutput()

Task 2: Create a copy of 1_app.R and create two tabs – i.e. tabPanel() – to show different plots either

  • using navbarPage layout with (example in 2b_app.R).
  • or tabsetPanel() (example in 2c_app.R).

Dynamic User Interface

Dynamic UI

  • Change the input options for users based on their input.
  • Useful for customising inputs, showing / hiding elements, and building flexible dashboards
  • Give more information to the user.
  • Make it easier to select from a long list of possible options, through step-by-step filtering.

Dynamic UI: Key Functions

There are three key techniques for creating dynamic user interfaces:

  • update: family of functions modifies parameters of input controls.
  • tabsetPanel() conditionally shows and hides parts of the user interface.
  • uiOutput() and renderUI() generates selected parts of the user interface.
  • Every input control, e.g. textInput(), is paired with an update function, e.g. updateTextInput(), that allows you to modify the control after it has been created.

Dynamic UI: Updating inputs

ui <- fluidPage(

  
  
  
  
  
  
  
  
  
)

server <- function(input, output) {
  
  
  
  
}

Dynamic UI: Updating inputs

ui <- fluidPage(

  numericInput(inputId = "max", 
               label = "Maximum", 
               value = 30),
  
  sliderInput(inputId = "n",
              label =  "n",
              value = 20,
              min = 0, 
              max = 100)
)

server <- function(input, output) {

  
  
  
}

Dynamic UI: Updating inputs

ui <- fluidPage(

  numericInput(inputId = "max", 
               label = "Maximum", 
               value = 30),
  
  sliderInput(inputId = "n",
              label =  "n",
              value = 20,
              min = 0, 
              max = 100)
)

server <- function(input, output) {
  observeEvent(input$max, {

    
  })
}

Dynamic UI: Updating inputs

ui <- fluidPage(

  numericInput(inputId = "max", 
               label = "Maximum", 
               value = 30),
  
  sliderInput(inputId = "n",
              label =  "n",
              value = 20,
              min = 0, 
              max = 100)
)

server <- function(input, output) {
  observeEvent(input$max, {
    updateSliderInput(inputId = "n",
                      max = input$max)
  })
}

Dynamic UI: Updating inputs

ui <- fluidPage(

  numericInput(inputId = "max", 
               label = "Maximum", 
               value = 30),
  
  sliderInput(inputId = "n",
              label =  "n",
              value = 20,
              min = 0, 
              max = 100)
)

server <- function(input, output) {
  observeEvent(input$max, {
    updateSliderInput(inputId = "n",
                      max = input$max)
  })
}




  • Example app (3_app.R): input (max) controls the max of slider.
  • observeEvent() triggers updateSliderInput() whenever max inputs changes.
  • Update function is linked to input control via inputId.
  • Task: Add an input to update the min value of the slider.

Exercise: Dynamic UI

ui <- fluidPage(
  sliderInput(inputId = "x1", label = "x1",  value = 0,  min = -10, max = 10),
  actionButton(inputId = "reset", label = "Reset")
)

server <- function(input, output) {
  observeEvent(input$reset, {
    updateSliderInput(inputId = "x1", value = 0)
  })
}
  • Reset parameters back to their initial value (see 4_app.R).
  • Task: Change this app so have two different slides one button for each slider that resets it to the starting value.

Dynamic UI: Hierarchical select boxes

  • Interactive drill down across multiple categories
  • Hierarchy in the data; e.g.
    • Eeach response modality contains multiple participants and images.
    • Half of the participants were familiarised with one set of images, the other half with the other set of images.

Dynamic UI: Hierarchical select boxes

ui <- fluidPage(
  selectInput("mod", "Modality",
              choices = mods),
  selectInput("ppt", "Participants",
              choices = NULL),
  plotOutput("plot")
)

server <- function(input, output) {

  
  
  
  
  
  
  
  
}

Dynamic UI: Hierarchical select boxes

ui <- fluidPage(
  selectInput("mod", "Modality",
              choices = mods),
  selectInput("ppt", "Participants",
              choices = NULL),
  plotOutput("plot")
)

server <- function(input, output) {
  sn_mod <- reactive({
    filter(spellname, 
           modality == input$mod)
  })

  
  
  
  
  
}

Dynamic UI: Hierarchical select boxes

ui <- fluidPage(
  selectInput("mod", "Modality",
              choices = mods),
  selectInput("ppt", "Participants",
              choices = NULL),
  plotOutput("plot")
)

server <- function(input, output) {
  sn_mod <- reactive({
    filter(spellname, 
           modality == input$mod)
  })
  observeEvent(sn_mod(), {

    
    
  })
  # [...]
}

Dynamic UI: Hierarchical select boxes

ui <- fluidPage(
  selectInput("mod", "Modality",
              choices = mods),
  selectInput("ppt", "Participants",
              choices = NULL),
  plotOutput("plot")
)

server <- function(input, output) {
  sn_mod <- reactive({
    filter(spellname, 
           modality == input$mod)
  })
  observeEvent(sn_mod(), {
    choices <- unique(sn_mod()$ppt_id)
    
    
  })
  # [...]
}

Dynamic UI: Hierarchical select boxes

ui <- fluidPage(
  selectInput("mod", "Modality",
              choices = mods),
  selectInput("ppt", "Participants",
              choices = NULL),
  plotOutput("plot")
)

server <- function(input, output) {
  sn_mod <- reactive({
    filter(spellname, 
           modality == input$mod)
  })
  observeEvent(sn_mod(), {
    choices <- unique(sn_mod()$ppt_id)
    updateSelectInput(inputId = "ppt",
                      choices = choices)
  })
  # [...]
}

Dynamic UI: Hierarchical select boxes

See 5_app.R: creates a user interface with two select boxes and one output plot.

  • Select a response modality
  • Select all responses of a participant
  • The choices for participant will be dynamically generated (choices = NULL).

Exercise: Hierarchical select boxes

The plot in 5_app.R compares familiarised and not familiarsed responses for the same image.

Task: Instead of filtering participants and plotting image names,

  • Filter image names depending and plot participants.
  • Add a sidebarLayout with input controls in the side panel and the plot in the main panel.

Start by creating a copy of 5_app.R; then change the code.

Dynamic UI: Dynamic visibility

  • See 6_app.R: Selectively show and hide parts of the UI.
  • Concealing optional UI with a tabset.
  • Show and hide UI as needed, without having to re-generate it from scratch.
  • Use tabset panel with hidden tabs.
  • Use updateTabsetPanel() to switch tabs from the server.
  • Task: Add a third panel to 6_app.R.

Dynamic UI: Conditional UI

  • App 7_app.R: simulate from the normal and exponential distributions.
  • Each distribution has different parameters so we’ll need some way to show different controls for different distributions.
  • Unique UI for each distribution will appear in its own tabPanel().
  • Arrange the two tabs into a tabsetPanel().
  • Task: Add in another parameter: the sample size which is currently hard coded as n

Dynamic UI: Creating UI with code

  • Update functions only allow you to change existing inputs, and a tabset only works if you have a fixed and known set of possible combinations.
  • Sometimes you need to create different types or numbers of inputs (or outputs), depending on other inputs.
  • So far, we have created a UI with code before the app started.
  • We will now create and modify UI while app is running.

Dynamic UI: Creating UI with code

ui <- fluidPage(
  selectInput("type", "type",
              c("slider", "numeric")),
  uiOutput("numeric")
)

server <- function(input, output) {

  
  
  
  
  
  
  
  
  
  
  
  
}

See 8a_app.R

  • uiOutput() inserts a placeholder in your ui.
  • This leaves a “hole” that your server code can later fill in.
  • renderUI() is called within server() to fill in the placeholder with dynamically generated UI.
  • Let’s add a third option for selectInput!

For an application see 8b_app.R which changes input method depending on variable type.

  • Whenever the selected variable is numeric, add a histogram of the selected variable or a barplot for anything else.

Dynamic UI: Creating UI with code

ui <- fluidPage(
  selectInput("type", "type",
              c("slider", "numeric")),
  uiOutput("numeric")
)

server <- function(input, output) {
  output$numeric <- renderUI({

    
    
    
    
    
    
    
    
    
    
  })
}

See 8a_app.R

  • uiOutput() inserts a placeholder in your ui.
  • This leaves a “hole” that your server code can later fill in.
  • renderUI() is called within server() to fill in the placeholder with dynamically generated UI.
  • Let’s add a third option for selectInput!

For an application see 8b_app.R which changes input method depending on variable type.

  • Whenever the selected variable is numeric, add a histogram of the selected variable or a barplot for anything else.

Dynamic UI: Creating UI with code

ui <- fluidPage(
  selectInput("type", "type",
              c("slider", "numeric")),
  uiOutput("numeric")
)

server <- function(input, output) {
  output$numeric <- renderUI({
    if (input$type == "slider") {

      
      
      
    } else {
      
            
            
            
    }        
  })
}

See 8a_app.R

  • uiOutput() inserts a placeholder in your ui.
  • This leaves a “hole” that your server code can later fill in.
  • renderUI() is called within server() to fill in the placeholder with dynamically generated UI.
  • Let’s add a third option for selectInput!

For an application see 8b_app.R which changes input method depending on variable type.

  • Whenever the selected variable is numeric, add a histogram of the selected variable or a barplot for anything else.

Dynamic UI: Creating UI with code

ui <- fluidPage(
  selectInput("type", "type",
              c("slider", "numeric")),
  uiOutput("numeric")
)

server <- function(input, output) {
  output$numeric <- renderUI({
    if (input$type == "slider") {
      sliderInput("dynamic", 
                  "Your choice", 
                  value = 0, 
                  min = 0, max = 10)
    } else {

      
      
      
      
  })
}

See 8a_app.R

  • uiOutput() inserts a placeholder in your ui.
  • This leaves a “hole” that your server code can later fill in.
  • renderUI() is called within server() to fill in the placeholder with dynamically generated UI.
  • Let’s add a third option for selectInput!

For an application see 8b_app.R which changes input method depending on variable type.

  • Whenever the selected variable is numeric, add a histogram of the selected variable or a barplot for anything else.

Dynamic UI: Creating UI with code

ui <- fluidPage(
  selectInput("type", "type",
              c("slider", "numeric")),
  uiOutput("numeric")
)

server <- function(input, output) {
  output$numeric <- renderUI({
    if (input$type == "slider") {
      sliderInput("dynamic", 
                  "Your choice", 
                  value = 0, 
                  min = 0, max = 10)
    } else {
      numericInput("dynamic", 
                   "Your choice", 
                   value = 0, 
                   min = 0, max = 10)
    }
  })
}

See 8a_app.R

  • uiOutput() inserts a placeholder in your ui.
  • This leaves a “hole” that your server code can later fill in.
  • renderUI() is called within server() to fill in the placeholder with dynamically generated UI.
  • Let’s add a third option for selectInput!

For an application see 8b_app.R which changes input method depending on variable type.

  • Whenever the selected variable is numeric, add a histogram of the selected variable or a barplot for anything else.

Filtering by a user-selected column

Update UI filter (sliderInput()) depending on which variable was selected.

ui <- fluidPage(
  selectInput("var", "Choose variable:", choices = names(spellname)),
  uiOutput("filterUI"),
)

server <- function(input, output){
 # Dynamically generate filter input based on selected variable
  output$filterUI <- renderUI({
    req(input$var)
    data <- spellname[[input$var]]
    rng <- range(data)
    sliderInput("filterVal", paste("Filter", input$var), min = rng[1], max = rng[2], value = rng)
  })
}

Filtering by a user-selected column

Base R

data[input$var > input$varRange[1] & input$var < input$varRange[2],]

tidyverse functions need !!sym(input$var) as in

filter(data, 
       !!sym(input$var) > input$varRange[1] & 
       !!sym(input$var) < input$varRange[2])

Dynamic filtering

  • Dynamically filter any data frame: 9_app.R
  • Each numeric variable gets a range slider and each factor variable will get a multi-select, so if a data frame has three numeric variables and two factors, the app will have three sliders and two select boxes.
  • Together, lets adapt this code for another dataset:
    • Use palmerpenguins::penguins
    • Then instead of showing a table (or in addition to), create a scatterplot that includes at least two numeric variables.

Recommended reading

References

Wickham, H. (2021). Mastering shiny. O’Reilly Media.