rguides

Getting Started with Shiny

Shiny is an R package that makes it incredibly easy to build interactive web applications directly from R. Whether you want to create a data dashboard, share interactive visualizations, or build a tool for your team, Shiny provides the framework to turn your R code into interactive web apps without requiring any HTML, CSS, or JavaScript knowledge.

In this tutorial, you’ll learn the fundamentals of Shiny: what it is, how to install it, the structure of a Shiny app, and how to create and run your first application.

What you’ll learn

This tutorial covers the key concepts and practical techniques for working with Getting Started with Shiny. By the end, you will know how to apply the core functions in real data analysis workflows.

What is Shiny?

Shiny is an R package developed by RStudio (now Posit) that lets you build interactive web applications entirely in R. It bridges the gap between R’s statistical capabilities and web technology, allowing you to:

  • Create interactive data dashboards
  • Build tools for data exploration
  • Share analysis results with non-R users
  • Create reproducible research prototypes
  • Deploy machine learning models as web services

The beauty of Shiny is that you don’t need to learn HTML, CSS, or JavaScript. You write all your logic in R, and Shiny handles the web interface automatically.

Installing Shiny

Getting started with Shiny is straightforward. Install it from CRAN using the install.packages() function:

install.packages("shiny")

Once installed, load the library in your R session:

library(shiny)

You’re now ready to build your first Shiny app.

Your first Shiny app

A minimal Shiny app requires just two components: a user interface (UI) and a server function. Here’s the classic “Hello World” example:

library(shiny)

ui <- fluidPage(
  "Hello, World!"
)

server <- function(input, output, session) {
}

shinyApp(ui, server)

When you run this code, Shiny starts a web server and opens your default browser displaying “Hello World!” Simple as that.

Understanding the UI and server

Every Shiny app follows the same basic structure: a UI object that defines the layout and inputs, and a server function that contains your R code and defines outputs.

The user interface (UI)

The UI defines what the user sees and interacts with. Shiny provides various layout functions, the most common being fluidPage(), which creates a responsive layout that adjusts to different screen sizes.

ui <- fluidPage(
  titlePanel("My First App"),
  
  sidebarLayout(
    sidebarPanel(
      sliderInput("bins", 
                  "Number of bins:", 
                  min = 1, 
                  max = 50, 
                  value = 30)
    ),
    
    mainPanel(
      plotOutput("distPlot")
    )
  )
)

This UI creates a page with a title, a sidebar with a slider input, and a main panel where a plot will be displayed.

The server function

The server function is where the magic happens. It’s where you read inputs, perform calculations, and define outputs:

server <- function(input, output, session) {
  
  output$distPlot <- renderPlot({
    
    # Generate bins based on input$bins from ui
    x    <- faithful[, 2]
    bins <- seq(min(x), max(x), length.out = input$bins + 1)
    
    # Draw the histogram
    hist(x, breaks = bins, col = 'darkgray', border = 'white')
    
  })
  
}

The server function receives three arguments:

  • input: A list of all input values from the UI
  • output: A list of objects that will be sent back to the browser
  • session: A session object for advanced control

You define outputs using render*() functions, where * is the type of output (plot, text, table, etc.). Each output in the UI must have a corresponding output$<name> defined in the server.

Running your app

There are three ways to run a Shiny app:

Method 1: run from an R script

Save your app code in an file called app.R and use:

shiny::runApp("app.R")

Method 2: run from the console

If you’ve defined ui and server in your R console, simply call:

shinyApp(ui = ui, server = server)

Method 3: use RStudio

In RStudio, simply press the “Run App” button in the toolbar when you have an app.R file open.

When you run a Shiny app, Shiny starts a local web server and automatically opens your default browser pointing to http://127.0.0.1:xxxxx. The app is fully functional and interactive.

A complete example: interactive histogram

Let’s put everything together with a complete example that creates an interactive histogram of the Old Faithful geyser data:

library(shiny)

# Define UI
ui <- fluidPage(
  titlePanel("Old Faithful Geyser Data"),
  
  sidebarLayout(
    sidebarPanel(
      sliderInput("bins",
                  "Number of bins:",
                  min = 5,
                  max = 50,
                  value = 30)
    ),
    
    mainPanel(
      plotOutput("distPlot")
    )
  )
)

# Define server
server <- function(input, output, session) {
  
  output$distPlot <- renderPlot({
    x    <- faithful$waiting
    bins <- seq(min(x), max(x), length.out = input$bins + 1)
    
    hist(x, 
         breaks = bins, 
         col = "steelblue", 
         border = "white",
         xlab = "Waiting time (minutes)",
         main = "Old Faithful Geyser Eruptions")
  })
  
}

# Run app
shinyApp(ui, server)

This example demonstrates several key concepts:

  • Using titlePanel() to add a title
  • Using sidebarLayout() for a standard dashboard layout
  • Using sliderInput() to create an interactive control
  • Using plotOutput() to display a plot
  • Using renderPlot() to generate the plot server-side
  • Reactivity: the plot automatically updates when you move the slider

Reactivity: how Shiny knows when to update

One of the most important concepts in Shiny is reactivity. When a user changes an input, Shiny automatically re-executes any code that depends on that input.

In our histogram example, the code inside renderPlot() depends on input$bins. When the user moves the slider, Shiny detects this dependency and re-runs the plot automatically. You don’t need to write any explicit update code—this is handled by Shiny’s reactive system.

The reactive programming model

Shiny’s core insight is reactive programming: UI inputs and server computations form a dependency graph. When an input changes, Shiny automatically re-runs only the computations that depend on it. reactive() creates a cached computed value. observe() runs side effects when dependencies change. eventReactive() and observeEvent() only respond to specific events (a button click), not all changes.

Building your first app

A minimal Shiny app has three components. ui defines the user interface. server defines the logic. shinyApp(ui, server) starts the app.

ui <- fluidPage(
  sliderInput("n", "Sample size", 10, 500, 100),
  plotOutput("hist")
)
server <- function(input, output) {
  output$hist <- renderPlot({
    hist(rnorm(input$n))
  })
}
shinyApp(ui, server)

Debugging Shiny apps

options(shiny.error = browser) drops into the R debugger when an error occurs in the server. browser() inside a reactive() or observe() breaks at that point. message("value: ", value) in server code prints to the R console, useful for inspecting reactive values. shinytest2 provides automated end-to-end tests that click buttons and check outputs.

What Shiny is and is not

Shiny is a web application framework for R. It converts R code into interactive web pages that run in a browser, where user inputs trigger R code to execute and the outputs update in response. The framework handles the web server, the JavaScript, and the communication protocol between browser and server, you write R code and Shiny takes care of making it interactive.

What Shiny is not is a general-purpose web framework. It is not suitable for building public websites with content marketing needs, for building APIs, or for applications where the interactive logic can run entirely in the browser without server involvement. Its strength is exposing data analysis and visualization that requires R computation to users who do not write R.

The reactive mental model

Shiny’s core abstraction is reactivity. When a user changes an input, all outputs that depend on that input automatically update. You do not write event handlers or update functions, you declare dependencies, and Shiny manages execution order. This is different from traditional UI programming, where you explicitly bind events to handlers.

Understanding when reactivity evaluates is critical to avoiding bugs. Reactive expressions are lazy: they do not compute until their value is needed. They are cached: the same reactive expression evaluated multiple times within one reactive cycle returns the same cached value. They invalidate: when an input they read changes, they discard their cached value and re-compute on the next evaluation request. These three properties together, lazy, cached, invalidating — define how reactive programming works.

Application structure

A Shiny application has two components: a user interface definition and a server function. The UI definition specifies the layout and the input and output placeholders. The server function contains the R code that computes outputs from inputs. The two components communicate through a shared namespace: outputs in the UI are referenced by the same IDs as the corresponding renderXxx calls in the server.

For applications that grow beyond a few hundred lines, splitting UI and server into separate files and organizing related functionality into Shiny modules keeps the code manageable. A module is a self-contained UI/server pair that handles one piece of the application’s functionality. Modules can be reused multiple times in the same application with different IDs, and they can be packaged for reuse across projects.

Next steps

Now that you’ve built your first Shiny app, here’s what to explore next:

  • More input controls: Shiny provides many input types including textInput(), selectInput(), checkboxInput(), dateInput(), and fileUpload()
  • Reactive expressions: Use reactive() to cache intermediate calculations and improve performance
  • Customizing the UI: Explore navbarPage() for multi-page apps and moduleServer() for reusable components
  • Shiny extensions: Check out shinythemes for styling, shinyWidgets for enhanced inputs, and htmlwidgets for integrating JavaScript libraries
  • Deployment: Share your apps using Shiny Server, Posit Connect, or shinapps.io

Shiny opens up a world of possibilities for creating interactive data applications. The skills you’ve learned here—understanding UI and server, using inputs and outputs, and leveraging reactivity—form the foundation for building more complex applications.