usethis::use_course("https://github.com/charliejhadley/training_shiny-module/raw/master/wdi_indicators_modules/01_without-modules.zip")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:
- The app displays data from the
WDIpackage - Each “page” details a different development indicator
- Users select a country of interest from a pull-down menu
- The chart, text and table update when a country is selected
- The charts, text and tables are the same on each page except for two variables: the selected country and the indicator
Shiny app without modules
Download the module-free version:
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.