Shiny
Build interactive web apps entirely in R.
40.1 A complete app
library(shiny); library(ggplot2); library(palmerpenguins)
ui <- fluidPage(
titlePanel("Penguin Explorer"),
sidebarLayout(
sidebarPanel(
selectInput("species", "Species", choices = levels(penguins$species), selected = "Adelie"),
selectInput("xvar", "X variable", choices = c("bill_length_mm", "bill_depth_mm", "flipper_length_mm")),
sliderInput("alpha", "Point transparency", min = 0.1, max = 1, value = 0.7),
checkboxInput("smooth", "Add trend line", TRUE)
),
mainPanel(
plotOutput("scatter"),
textOutput("summary"),
tableOutput("table")
)
)
)
server <- function(input, output, session) {
filtered <- reactive({
subset(penguins, species == input$species & !is.na(body_mass_g))
})
output$scatter <- renderPlot({
p <- ggplot(filtered(), aes(.data[[input$xvar]], body_mass_g)) +
geom_point(alpha = input$alpha)
if (input$smooth) p <- p + geom_smooth(method = "lm")
p
})
output$summary <- renderText({
paste("Showing", nrow(filtered()), "penguins")
})
output$table <- renderTable(head(filtered(), 5))
}
shinyApp(ui, server)40.2 Reactivity
input$id (reactive values set by widgets) output$id <- render*() (renderPlot, renderTable, renderText, renderUI, renderDataTable)
reactive() — cached, lazy derived values observe(), observeEvent(input$btn, {...}) — side effects
eventReactive(input$go, {...}), bindEvent() reactiveVal(), reactiveValues()
isolate(), req() (stop if missing), validate(need(...)), debounce(), throttle()
updateSelectInput(), insertUI(), removeUI(), showNotification(), showModal()
40.3 Structuring larger apps
Modules (moduleServer(), NS()) for reusable components.
bslib for modern Bootstrap themes and layouts (page_sidebar(), card(), value_box()).
Frameworks: golem, rhino (Appsilon), leprechaun.
Testing: testServer(), shinytest2 for end-to-end snapshot tests.
Extensions: shinyWidgets, shinyjs, DT, plotly, leaflet, waiter, bslib.
40.4 Deployment
shinyapps.io, Posit Connect (Cloud), Shiny Server (open source), Docker, Shinylive (runs entirely in the browser via webR, no server needed), Hugging Face Spaces.