Shiny Modules

Shiny modules help you to organise complex apps as you can split your code, similarly to functions, into more manageable bits. The big difference is that you can tackle the UI- and server-side simultaneously. Once you have your module, you can use it even multiple times! In this post, I will give you a short intro and an example of how you can use modules in your application.

The following block is the app where we will introduce modules. First, we need to identify what we actually want to put into a module. It makes sense that it is some closed part, as the elements in the tab "table". The idea is that the whole box element and the corresponding server logic is handled in the module. I recommend writing the module code in a separate file, e.g. in module_tables.R.

library(shiny)
library(shinydashboard)
library(dplyr)

data("iris")

ui <- dashboardPage(
    dashboardHeader(title = "Example Module"),
    dashboardSidebar(
        sidebarMenu(id = "tabs",
                    menuItem("Table",
                             tabName = "table",
                             icon = icon("table")
                             )
                    )
        ),
    dashboardBody(
        tabItems(
            tabItem(
                tabName = "table",
                box(title = "Lengths",
                    width = 8,
                    selectInput("species", "species",
                                choices = c("setosa", "versicolor", "virginica")),
                    plotOutput("sepal_petal")
                    )
                )
            )
        )
    )

server <- function(input, output, session) {
    output$sepal_petal <- renderPlot({
        df_subset <- iris %>%
            filter(Species %in% input$species)
        plot(main = paste0("Species: ", input$species),
             x = df_subset$Sepal.Length,
             y = df_subset$Petal.Length,
             xlab = "Sepal Length",
             ylab = "Petal Length")
    })
}

shinyApp(ui = ui, server = server)

UI

In the new module_tables.R file, we first write a function for the UI part. For me, it helps to end the name of these functions with an “_ui”, just for readability. The function will look like this where three main characteristics are to consider:

table_ui <- function(id) {
  ns <- NS(id)
  tagList(
    box(title = "Lengths",
        width = 8,
        selectInput(ns("species"), 
                    label = "Species", 
                    choices = c("setosa", "versicolor", "virginica")),
        plotOutput(ns("sepal_petal")
                   )
        )
    )
}
  • The first thing to notice is the id argument and in the second line the NS(id). It creates the namespace for the module, an encapsulated environment that reduces naming problems.
  • All your UI elements need to be wrapped by tagList.
  • The inputId for your UI elements need to be wrapped in ns(). It adds a prefix to all of your inputIds. Otherwise your server-side will have some troubles.

Server

The server part is the second part of the module and seems a bit more complicated with the two-layered function. However, you’ll see that beyond that, it is not that different from what we know from the server part of the normal app.

table_server <- function(id) {
  moduleServer(id, function(input, output, session) {
    
    output$sepal_petal <- renderPlot({
      df_subset <- iris %>% 
        filter(Species %in% input$species)
      plot(main = paste0("Species: ", input$species),
           x = df_subset$Sepal.Length, 
           y = df_subset$Petal.Length,
           xlab = "Sepal Length",
           ylab = "Petal Length")
    })
    }
  )
}
  • Similarly to the ui-function, the id argument needs to be passed (I end these functions with an “_server” ).
  • The moduleServer makes the whole thing a module and takes care of the namespace part. But actually, you wont need to worry too much about this.
  • The rest is the same code as in the original app.

After you’ve written your module and included a line to source the file in the app.R, you’ll need to invoke the two functions at the right place. The UI part of course where the box element was before and the server part in the server function. It’s as simple as that.

library(shiny)
library(shinydashboard)
library(dplyr)

data("iris")
# to source your module file
source("~/blog/dashboards/example_module/modules/module_table.R")

ui <- dashboardPage(
    dashboardHeader(title = "Example Module"),

    dashboardSidebar(
        sidebarMenu(
            menuItem("Table",
                     tabName = "table",
                     icon = icon("table")
                     )
            )
        ),
    dashboardBody(
        tabItems(
            tabItem(tabName = "table",
                    # UI part of your module
                    table_ui("table_module")
                    )
            )
        )
)

server <- function(input, output, session) {
    # server part of your module
    table_server("table_module")
}

shinyApp(ui = ui, server = server)

In this manner, you can neatly structure your code. If you add new tabItems, you can repeat this process and you’ll have the content for each tab independent of each other. Below you’ll see what you need to copy+paste for a new module.

module_ui <- function(id) {
  ns <- NS(id)
  tagList()
}

module_server <- function(id) {
  moduleServer(id, function(input, output, session) {
    }
  )
}

Of course, there is way more to explore and possible with modules. Wait for a new post;) or you can have a look at the following resources:

This post first appeared on rshiny.blog.

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: