12  Advanced Shiny

Author
Affiliation

Dr Randy Johnson

Hood College

Published

February 19, 2025

Acknowledgements

Today’s material comes largely from Layouts, themes, HTML. Gemini Code Assist was also active during the development of these materials, and some text was generated with its assistance.

Review

Before we dive into the new material, let’s review what we covered previously.

  • ui: The front-end (what the user sees)
  • server: The back-end (the R logic)
  • Reactivity
  • Layouts

UI IO

  • *Input: User inputs (e.g. text, sliders, select)
  • *Output / render*: Outputs generated by the server (e.g. text, plot, table)

Server

  • Use input to access user inputs
  • Use output to access outputs for the UI
  • Render outputs using render* functions (e.g. renderText, renderPlot)

Reactivity

  • Reactive expressions: Variables that automatically update when their inputs change
    • Use reactive() to create them
    • Access them like regular functions (e.g. my_reactive())

Observers

  • Observers: Functions that run when their inputs change, but don’t return a value
    • Use observeEvent() to create them
    • Useful for side effects (e.g. updating UI elements, printing to console)

For example, suppose we want to display only a shorter list of genes based on the chromosomal region selected by the user (i.e. rather than every gene in the genome).

library(shiny)

ui <- fluidPage(
  selectInput("chrom", "Select Chromosome:", choices = c("Xp22.33", "Xp22.32", "Xp22.31", ...)),
  selectInput("gene", "Select Gene:", choices = NULL) # Starts empty
)

server <- function(input, output, session) {
  
  # The Observer: It watches 'input$chrom' and triggers a side effect
  observeEvent(input$chrom, {
    # 1. Logic to determine new choices (usually based on a dataframe)
    new_choices <- get_genes_for_chrom(input$chrom) 
    
    # 2. The Side Effect: Changing the UI state without 'returning' a value
    updateSelectInput(session, "gene", choices = new_choices)
  })
}

Single-page Layouts

  • fluidPage(): A fluid layout that adjusts to the size of the browser window
    • titlePanel(): Add a title at the top
    • sidebarLayout(): A layout with a sidebar and main area
      • sidebarPanel(): The sidebar area for inputs
      • mainPanel(): The main area for outputs

Image courtesy of Wickham (2021)

Example: Central Limit Theorem

ui <- fluidPage(
  titlePanel("Central limit theorem"),
  sidebarLayout(
    sidebarPanel(
      numericInput("m", "Number of samples:", 2, min = 1, max = 100)
    ),
    mainPanel(
      plotOutput("hist")
    )
  )
)
server <- function(input, output, session) {
  output$hist <- renderPlot({
    means <- replicate(1e4, mean(runif(input$m)))
    hist(means, breaks = 20)
  }, res = 96)
}

Multi-row Layouts

  • fluidRow(): Create a new row in the layout
    • column(): Define columns within a row (total width is 12 columns)
    • Use multiple fluidRow() and column() calls to create complex layouts

Image courtesy of Wickham (2021)

Multi-page Layouts

  • tabsetPanel(): Create a tabbed interface (within fluidPage())
    • tabPanel(): Define individual tabs within the panel
    • Tabs are displayed horizontally at the top of the page
  • navlistPanel(): Create a navigation list on the left side of the page (within fluidPage())
    • tabPanel(): Define individual tabs within the navlist
    • Tabs are displayed vertically on the left side of the page
  • navbarPage(): Create a navigation bar at the top of the page (replaces fluidPage())
    • tabPanel(): Define individual pages within the navbar
    • navbarMenu(): Create a dropdown menu within the navbar for additional pages
    • Tabs are displayed horizontally at the top of the page, with dropdowns for tab menus

tabsetPanel Example: Uniform/Normal Distributions

library(shiny)

ui <- fluidPage(
  titlePanel("Tabbed Interface"),
  tabsetPanel(
    tabPanel("Uniform", plotOutput("plot1")),
    tabPanel("Normal", plotOutput("plot2"))
  )
)

server <- function(input, output, session) {
  output$plot1 <- renderPlot({
    hist(runif(100))
  }, res = 96)
  
  output$plot2 <- renderPlot({
    hist(rnorm(100))
  }, res = 96)
}

shinyApp(ui, server)

Themes

The bslib package allows you to customize the appearance of your Shiny app using Bootstrap themes. You can choose from pre-built themes or create your own.

  • Add theme = bs_theme(...) to your fluidPage() to apply a pre-built theme

Shiny Themes

  • Example: use theme = bs_theme(bootswatch = "cosmo") to apply the “Cosmo” theme from Bootswatch

Figure 12.1: Example bslib themes from Wickham (2021)

Customization

You can also customize the theme, for example:

bslib::bs_theme(
  bg = "#0b3d91", 
  fg = "white", 
  base_font = "Source Sans Pro"
)

Themed Plots

Calling thematic::thematic_shiny() in your server function will automatically apply the current theme to your plots.

library(shiny)
library(ggplot2)

ui <- fluidPage(
  theme = bslib::bs_theme(bootswatch = "darkly"),
  titlePanel("A themed plot"),
  plotOutput("plot"),
)

server <- function(input, output, session) {
  thematic::thematic_shiny()
  
  output$plot <- renderPlot({
    ggplot(mtcars, aes(wt, mpg)) +
      geom_point() +
      geom_smooth()
  }, res = 96)
}

shinyApp(ui, server)

Further Customization

  • For more advanced customization, you can add custom HTML, CSS or JavaScript to your Shiny app
  • Hadley Wickham has promised that there will be similar thematic customization options for Quarto documents in the future

Review Questions

  • Provide an example of a “side effect” that would justify using an observer instead of a standard output.
  • What does the function fluidPage() do to ensure a Shiny app looks good on both a laptop and a large monitor?
  • When using fluidRow() and column(), the maximum total width value that the columns in a single row add up to is 12. Why was the number 12 chosen instead of something larger or smaller?
  • Why would a developer choose navbarPage() instead of fluidPage() when starting their ui definition?
  • If you apply a dark theme using bslib, why might you also need to call thematic::thematic_shiny() in the server function?

References

Wickham, Hadley. 2021. Mastering Shiny: Build Interactive Apps, Reports, and Dashboards Powered by R. First edition. Beijing Boston Farnham Sebastopol Tokyo: O’Reilly.