# moduleServer style Shiny Module
# geyser/moduleServer/app.R
# This uses the `id` argument to connect UI and Server components.
# New (>=2020) style: `moduleServer`
# Function `geyserServer` returns a `server` function.
# Uses shiny `id` to connect serer components.
# Explicit function assignment to create `server` is crucial.
# See <https://mastering-shiny.org/scaling-modules.html>.
source("moduleServer.R")
ui <- bslib::page(
geyserInput(id = "geyser"),
geyserOutput(id = "geyser"),
# Display this only if the density is shown
geyserUI(id = "geyser")
)
server <- function(input, output, session) {
geyserServer(id = "geyser")
}
shiny::shinyApp(ui, server)Build Module (Shinylive)
A serverless WebAssembly-powered R Shiny app running directly in the browser using the package’s modular code, with full source tabs.
This section displays a geyser app using 4_moduleServer, powered directly in the page via the serverless Shinylive (WebAssembly).
The live application is run completely serverless in your browser. Below the application, view the R source files for both the main application (app.R) and the custom module (moduleServer.R) in separate tabs.
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| viewerHeight: 600
#| components: [viewer]
library(shiny)
library(bslib)
# --- Module definitions (from moduleServer.R) ---
geyserServer <- function(id) {
shiny::moduleServer(id, function(input, output, session) {
ns <- session$ns
# Output Main Plot
output$main_plot <- shiny::renderPlot({
graphics::hist(faithful$eruptions,
probability = TRUE,
breaks = as.numeric(input$n_breaks),
xlab = "Duration (minutes)",
main = "Geyser eruption duration"
)
if (input$individual_obs) {
graphics::rug(faithful$eruptions)
}
if (input$density) {
shiny::req(input$bw_adjust)
dens <- stats::density(faithful$eruptions,
adjust = input$bw_adjust
)
graphics::lines(dens, col = "blue")
}
})
# Input Bandwidth Adjustment
output$bw_adjust_ui <- shiny::renderUI({
if (input$density) {
shiny::sliderInput(
inputId = ns("bw_adjust"),
label = "Bandwidth adjustment:",
min = 0.2, max = 2, value = 1, step = 0.2
)
}
})
})
}
geyserInput <- function(id) {
ns <- shiny::NS(id)
shiny::tagList(
shiny::selectInput(
inputId = ns("n_breaks"),
label = "Number of bins in histogram (approximate):",
choices = c(10, 20, 35, 50),
selected = 20
),
shiny::checkboxInput(
inputId = ns("individual_obs"),
label = shiny::strong("Show individual observations"),
value = FALSE
),
shiny::checkboxInput(
inputId = ns("density"),
label = shiny::strong("Show density estimate"),
value = FALSE
)
)
}
geyserOutput <- function(id) {
ns <- shiny::NS(id)
shiny::plotOutput(ns("main_plot"), height = "300px")
}
geyserUI <- function(id) {
ns <- shiny::NS(id)
shiny::uiOutput(ns("bw_adjust_ui"))
}
# --- App entry point (from app.R) ---
ui <- bslib::page(
geyserInput(id = "geyser"),
geyserOutput(id = "geyser"),
# Display this only if the density is shown
geyserUI(id = "geyser")
)
server <- function(input, output, session) {
geyserServer(id = "geyser")
}
shiny::shinyApp(ui, server)
Source Code
#' Shiny Server for geyser Example
#'
#' @param input,output,session shiny server reactives
#' @return reactive server
#' @export
#' @rdname geyser
#' @importFrom shiny bootstrapPage checkboxInput moduleServer NS plotOutput
#' renderPlot renderUI selectInput shinyApp sliderInput uiOutput
#' @importFrom graphics hist lines rug
#' @importFrom stats density
geyserServer <- function(id) {
shiny::moduleServer(id, function(input, output, session) {
ns <- session$ns
# Output Main Plot
output$main_plot <- shiny::renderPlot({
graphics::hist(faithful$eruptions,
probability = TRUE,
breaks = as.numeric(input$n_breaks),
xlab = "Duration (minutes)",
main = "Geyser eruption duration")
if (input$individual_obs) {
graphics::rug(faithful$eruptions)
}
if (input$density) {
shiny::req(input$bw_adjust)
dens <- stats::density(faithful$eruptions,
adjust = input$bw_adjust)
graphics::lines(dens, col = "blue")
}
})
# Input Bandwidth Adjustment
output$bw_adjust_ui <- shiny::renderUI({
if(input$density) {
shiny::sliderInput(inputId = ns("bw_adjust"),
label = "Bandwidth adjustment:",
min = 0.2, max = 2, value = 1, step = 0.2)
}
})
})
}
#' Shiny Module Input for Geyser
#' @param id identifier for shiny reactive
#' @return nothing returned
#' @rdname geyser
#' @export
geyserInput <- function(id) {
ns <- shiny::NS(id)
shiny::tagList(
shiny::selectInput(inputId = ns("n_breaks"),
label = "Number of bins in histogram (approximate):",
choices = c(10, 20, 35, 50),
selected = 20),
shiny::checkboxInput(inputId = ns("individual_obs"),
label = shiny::strong("Show individual observations"),
value = FALSE),
shiny::checkboxInput(inputId = ns("density"),
label = shiny::strong("Show density estimate"),
value = FALSE))
}
#' Shiny Module Output for Geyser
#' @param id identifier for shiny reactive
#' @return nothing returned
#' @rdname geyser
#' @export
geyserOutput <- function(id) {
ns <- shiny::NS(id)
shiny::plotOutput(ns("main_plot"), height = "300px")
}
#' Shiny Module UI for Geyser
#' @param id identifier for shiny reactive
#' @return nothing returned
#' @rdname geyser
#' @export
geyserUI <- function(id) {
ns <- shiny::NS(id)
shiny::uiOutput(ns("bw_adjust_ui"))
}
#' Shiny Module App for Geyser
#' @return nothing returned
#' @rdname geyser
#' @export
geyserApp <- function() {
ui <- bslib::page(
geyserInput("geyser"),
geyserOutput("geyser"),
# Display this only if the density is shown
geyserUI("geyser")
)
server <- function(input, output, session) {
geyserServer("geyser")
}
shiny::shinyApp(ui, server)
}