All About Modules

rstudio::conf(2022)
Building Production-Quality Shiny Applications

Eric Nantz

What are Modules?

Building blocks to compose any Shiny app out of smaller, more understandable pieces

  • Avoids namespace collisions when using same widget across different areas of your app
  • Allow you to encapsulate distinct app interfaces
  • Organize code into logical and easy-to-understand components
  • Facilitate collaboration

Sound familiar?

  • R functions also help avoid collisions in variable names with general R code
  • Essential for creating non-trivial and extensive workflows

Anatomy of a Function (UI)

artUI <- function() {
  tagList(
    checkboxInput(
      "input1",
      "Check Here"
    ),
    selectInput(
      "input2",
      "Select Object",
      choices = c("jar", "vase"),
      selected = "jar",
      multiple = FALSE
    ),
    plotOutput("plot1")
  )
}

Anatomy of a Module (UI)

artUI <- function(id) {
  ns <- NS(id)
  tagList(
    checkboxInput(
      ns("input1"),
      "Check Here"
    ),
    selectInput(
      ns("input2"),
      "Select Object",
      choices = c("jar", "vase"),
      selected = "jar",
      multiple = FALSE
    ),
    plotOutput(ns("plot1"))
  )
}

Anatomy of a Module (UI)

artUI <- function(id) {
  ns <- NS(id)
  tagList(
    checkboxInput(
      ns("input1"),
      "Check Here"
    ),
    selectInput(
      ns("input2"),
      "Select Object",
      choices = c("jar", "vase"),
      selected = "jar",
      multiple = FALSE
    )
  )
}
  • id: String to use for namespace
  • ns <- NS(id): Create proper namespace function

Anatomy of a Module (Server)

artServer <- function(input, output, session) {
  df <- reactive({
    # do something fancy
  })
  
  output$plot1 <- renderPlot({
    ggplot(df(), aes(x = x, y = y)) +
      geom_point()
  })
}

Anatomy of a Module (Server)

artServer <- function(id) {
  moduleServer(
    id,
    function(input, output, session) {
      df <- reactive({
        # do something fancy
      })
      
      output$plot1 <- renderPlot({
        ggplot(df(), aes(x = x, y = y)) +
          geom_point()
      })
    }
  )
}

Minimal changes necessary

Anatomy of a Module (Server)

artServer <- function(id) {
  moduleServer(id,
    function(input, output, session) {
      df <- reactive({
        # do something fancy
      })
      
      output$plot1 <- renderPlot({
        ggplot(df(), aes(x = x, y = y)) +
          geom_point()
      })
    }
  )
}

🤔 id

  • `moduleServer(): Encapsulate server-side logic with namespace applied.

Invoking Modules

ui <- fluidPage(
  fluidRow(
    artUI("mod1")
  )
)

server <- function(input, output, session) {
  artServer("mod1")
}

shinyApp(ui, server)

Giving and Receiving

artUI <- function(id, choices = c("jar", "vase")) {
  ns <- NS(id)
  tagList(
    checkboxInput(
      ns("input1"),
      "Check Here"
    ),
    selectInput(
      ns("input2"),
      "Select Object",
      choices = choices,
      selected = choices[1],
      multiple = FALSE
    ),
    plotOutput(ns("plot1"))
  )
}
  • Reasonable inputs: static values, vectors, flags
  • Avoid reactive parameters
  • Return value: tagList() of inputs, output placeholders, and other UI elements

Giving and Receiving

artServer <- function(id, df, title = "My Plot") {
  moduleServer(id,
    function(input, output, session) {
      user_selections <- reactive({
        list(input1 = input$input1, input2 = input$input2)
      })
      
      output$plot1 <- renderPlot({
        ggplot(df(), aes(x = x, y = y)) +
          geom_point() +
          ggtitle(title)
      })
      
      user_selections
    }
  )
}
  • Input parameters (and return values) can be a mix of static and reactive objects

To () or not to ()

# app server
df <- reactive({
  art_data |>
    filter(dept == input$dept)
})

artServer("mod1", df)
artServer <- function(id, df, title = "Amazing") {
  moduleServer(id,
    function(input, output, session) {
      user_selections <- reactive({
        list(input1 = input$input1,
             input2 = input$input2)
      })
      
      output$plot1 <- renderPlot({
        ggplot(df(), aes(x = x, y = y)) +
          geom_point() +
          ggtitle(title)
      })
      
      user_selections
    }
  )
}
  • Reactive parameters reference by name: df
  • Inside module, invoke reactive parameter as you would any other reactive in Shiny: df()
  • Any reactive(s) returned by module should also be reference by name: user_selections, user_selections()

Your Turn: Exercise 1

Create a Shiny module for the core image viewer portion of the application in ex-1/app.R

10:00