Skip to content
AgileLogics
8 min read

Interactive Web Apps with Shiny

Prerequisites: Data visualization with ggplot2, Functions

Shiny turns R code into interactive web apps — dashboards, data explorers, calculators — without writing HTML, CSS, or JavaScript yourself.

install.packages("shiny")

A complete app

Save this as app.R and click Run App in RStudio:

library(shiny)
library(ggplot2)

ui <- fluidPage(
  titlePanel("Fuel economy explorer"),
  sidebarLayout(
    sidebarPanel(
      selectInput("class", "Car class", choices = sort(unique(mpg$class))),
      sliderInput("year", "Model year", min = 1999, max = 2008, value = c(1999, 2008), sep = "")
    ),
    mainPanel(
      plotOutput("scatter"),
      textOutput("summary")
    )
  )
)

server <- function(input, output, session) {
  filtered <- reactive({
    subset(mpg, class == input$class & year >= input$year[1] & year <= input$year[2])
  })

  output$scatter <- renderPlot({
    ggplot(filtered(), aes(displ, hwy)) +
      geom_point(size = 3, alpha = 0.7) +
      labs(x = "Engine size (L)", y = "Highway MPG")
  })

  output$summary <- renderText({
    sprintf("%d cars, average %.1f highway MPG", nrow(filtered()), mean(filtered()$hwy))
  })
}

shinyApp(ui, server)

How it works

  • The UI describes the page: inputs (selectInput, sliderInput) and placeholders for outputs (plotOutput, textOutput).
  • The server function computes outputs. input$class reads the current value of the input with id "class".
  • render*() functions create outputs; each is assigned to output$<id> matching a placeholder in the UI.
  • Reactivity: Shiny tracks which inputs each output uses, and reruns only what's affected when an input changes. reactive() creates a shared, cached calculation — here, the filtered data used by both the plot and the text.

Growing an app

  • Layouts and themes — the bslib package provides modern layouts (page_sidebar, cards, value boxes) and Bootstrap themes.
  • Modules — split a large app into reusable components, each with its own UI and server functions.
  • Interactivity — DT for sortable tables; plotly or ggiraph for interactive charts.
  • Testing — shinytest2 records and replays app sessions; keep data logic in plain functions tested with testthat.

Deploying

Publish to Posit Connect Cloud or shinyapps.io directly from RStudio, run your own Shiny Server or Posit Connect, or package the app in a Docker container. Shinylive can even run some apps entirely in the browser, with no server.

Common mistakes

  • Doing heavy work (reading big files, fitting models) inside a render*() function that reruns on every change. Load data once, at the top of the app, and cache expensive steps with reactive().
  • Forgetting that outputs must use the same id in the UI and the server.