Shiny's module feature lets you break complicated UI and server logic into smaller, self-contained pieces. Compared to large monolithic Shiny apps, modules are easier to reuse and easier to reason about. See the article at https://shiny.posit.co/articles/modules.html to learn more.
moduleServer(id, module, session = getDefaultReactiveDomain())The return value, if any, from executing the module server function
Starting in Shiny 1.5.0, we recommend using moduleServer instead of
callModule(), because the syntax is a little easier
to understand, and modules created with moduleServer can be tested with
testServer().
When module UI is added and removed dynamically (e.g. via insertUI() and
removeUI()), the server-side reactive objects created by moduleServer()
continue to run after the UI is removed. The parent inserted the module's
UI under an id, so it can tear down all reactive values, expressions, and
observers in that scope by that same id:
If teardown must happen somewhere that doesn't have the parent session or
id, a module can instead return its own session$destroy as a handle:
myModuleServer <- function(id) {
moduleServer(id, function(input, output, session) {
# ... module logic ...
# Return a cleanup function for the caller to invoke
list(result = ..., destroy = session$destroy)
})
}
mod <- myModuleServer("myid")
removeUI(selector = "#myid")
mod$destroy()See the session help topic for details on composability and data ownership
patterns when using session$destroy().
# Define the UI for a module
counterUI <- function(id, label = "Counter") {
ns <- NS(id)
tagList(
actionButton(ns("button"), label = label),
verbatimTextOutput(ns("out"))
)
}
# Define the server logic for a module
counterServer <- function(id) {
moduleServer(
id,
function(input, output, session) {
count <- reactiveVal(0)
observeEvent(input$button, {
count(count() + 1)
})
output$out <- renderText({
count()
})
count
}
)
}
# Use the module in an app
ui <- fluidPage(
counterUI("counter1", "Counter #1"),
counterUI("counter2", "Counter #2")
)
server <- function(input, output, session) {
counterServer("counter1")
counterServer("counter2")
}
if (interactive()) {
shinyApp(ui, server)
}
# If you want to pass extra parameters to the module's server logic, you can
# add them to your function. In this case `prefix` is text that will be
# printed before the count.
counterServer2 <- function(id, prefix = NULL) {
moduleServer(
id,
function(input, output, session) {
count <- reactiveVal(0)
observeEvent(input$button, {
count(count() + 1)
})
output$out <- renderText({
paste0(prefix, count())
})
count
}
)
}
ui <- fluidPage(
counterUI("counter", "Counter"),
)
server <- function(input, output, session) {
counterServer2("counter", "The current count is: ")
}
if (interactive()) {
shinyApp(ui, server)
}