Shiny modules to reduce duplication in apps

A practical introduction to Shiny modules via a World Development Indicators app — showing how modules reduce duplication when adding new tabs and controls.
R
Published

July 9, 2019

Shiny apps are awesome, but at some point the subject of “shiny modules” will rear its head.

When I started learning modules, I found most of the existing articles focus on the technical side and don’t focus on the benefits of using modules — they can significantly simplify and improve your apps.

We’re going to build this Shiny app:

Shiny app without modules

Download the module-free version:

usethis::use_course("https://github.com/charliejhadley/training_shiny-module/raw/master/wdi_indicators_modules/01_without-modules.zip")

The ui.R file duplicates the selectInput() for each tab:

navbarPage(
  "WDI",
  tabPanel("Internet",
    fluidPage(selectInput("internet_country", choices = countries_list), ...)),
  tabPanel("Bank branches",
    fluidPage(selectInput("bank_branches_country", choices = countries_list), ...)),
  ...
)

The server.R file duplicates render*() functions for each tab:

function(input, output, session) {

  output$internet_timeline <- renderPlot({
    wdi_data |> gg_wdi_indicator_timeline(input$internet_country, ...)
  })

  output$internet_comparison_table <- renderUI({
    wdi_data |> filter(country == input$internet_country) |> datatable(...)
  })

  output$bank_branches_timeline <- renderPlot({
    wdi_data |> gg_wdi_indicator_timeline(input$bank_branches_country, ...)
  })

  output$bank_branches_comparison_table <- renderUI({
    wdi_data |> filter(country == input$bank_branches_country) |> datatable(...)
  })

}

Adding a new tab (e.g. secondary schools) requires duplicating all of the above again.

Benefits of switching to modules

  • Reduced script file length — easier to read and understand
  • Simpler feature updates — changing the module code updates all pages at once
  • Fewer transcription errors — no copy/paste of nearly identical code blocks
  • Encapsulation — each tab’s logic is self-contained and independently testable

Without modules, changing the look of the comparison tables requires editing every output$*_comparison_table object. With modules, you change it once.

More tutorials on real-world Shiny module patterns are planned — links will be added here as they’re published.