Chapter 1 Introduction

Do you want to provide interactive visualisation and data exploration features for users who do not have R and data science skills? Discover how easy it can be to use R and shiny to create your own apps and dashboards for exploring data without relying on web development or external BI tools. You will be shown various examples of input widgets and outputs to display tables and visualisations.

This book is designed to accompany the Introduction to R Shiny training that we run at DfT. To complete this course, you will need a Cloud R account.

Users of this book should feel comfortable with using R and basic graphics with ggplot2.

If you’re running through this book solo, it is recommended to clone the exercise repository, run through the book in order and try out all the of the exercises as you go through. Each exercise has its own folder with an exercise and solution script, which allows you to view prompts to help with the question and run the final solution.

1.1 Session aims

  • understand what a Shiny application is
  • understand the back-end of building an app
  • build a basic user interface

1.2 What is a Shiny application?

Shiny is an R package (similar to dplyr or data.table), although it is often treated like it is a completely separate platform.

It provides functionality to produce Shiny apps; for the user, these are essentially web pages running in HTML/CSS/JS. However, it is coded within R, so there is no need to learn any of those languages.

Shiny apps link to a computer running R to do all of the data processing. This makes them very powerful, as it allows R to calculate values “on the fly” i.e. as the user makes requests.

It goes way beyond a dashboarding tool; you can use it to build complicated tools and apps for a range of purposes.

1.3 Why Shiny tools?

  • beyond dashboarding: Shiny can be used to create dashboards, but it actually goes far beyond that to allow you to create a whole range of custom apps and tools using R code
  • calculating values on the fly: Shiny is ideal for scenarios where you want greater user interactivity. Rather than just filtering data, new values can be calculated based on user inputs, and used or exported
  • use of libraries: Shiny allows you to tap into the full range of libraries available in R, so you can build a Shiny app for anything there’s a library for
  • no limits: In tools such as PowerBI, it’s relatively easy to hit the barrier of what the software can’t do. In Shiny, you’re generally only limited by what you can’t do
  • low barrier to entry: Don’t need to learn HTML/JavaScript to use it

The DfT’s Road Safety Statistics: Collision Analysis Tool was built using R and R Shiny.

1.4 Getting started with a Shiny app

Once you have the shiny library installed, you can generate a basic app from a template within R Studio.

From there, you can:

  • provide a name for the app
  • choose a location
  • specify whether you want it over one file or two (in this course, you will need to choose two)

1.4.1 How to run a Shiny app

Shiny code does not run like normal R code, you can’t run it one line at a time. Instead, you run the code as a single chunk in an interactive environment. When you’re using a Shiny application, the normal “Run” button will change to a “Run App” button.

Click on the dropdown arrow next to the “Run App” to choose how you want to run the app. Some features will not work when running in window or viewer pane, therefore it is recommended to run your apps in an external browser. This is done by selecting the “Run External” option.

1.4.2 Exercise

10:00

  1. Make sure you have got shiny installed in R. (Hint: If not run install.packages("shiny")).
  2. Launch a new Shiny web app. Give the application the name “test_shiny” and make sure it is a multi-file app.
  3. Run the app in an external window; what does the app do?
  4. Look in the “test_shiny” folder that was produced when you created a new Shiny app. How many files are in there, and what are they called?
  5. Can you see the chart without opening the app?
  6. What do you notice when you try to run any other code while the Shiny app is running?
1.4.2 Solution


UI

#
# This is the user-interface definition of a Shiny web application. You can
# run the application by clicking 'Run App' above.
#
# Find out more about building applications with Shiny here:
# 
#    http://shiny.rstudio.com/
#

library(shiny)

# Define UI for application that draws a histogram
shinyUI(fluidPage(
  
  # Application title
  titlePanel("Old Faithful Geyser Data"),
  
  # Sidebar with a slider input for number of bins 
  sidebarLayout(
    sidebarPanel(
       sliderInput("bins",
                   "Number of bins:",
                   min = 1,
                   max = 50,
                   value = 30)
    ),
    
    # Show a plot of the generated distribution
    mainPanel(
      plotOutput("distPlot")
      )
    )
  )
)

Server

#
# This is the server logic of a Shiny web application. You can run the 
# application by clicking 'Run App' above.
#
# Find out more about building applications with Shiny here:
# 
#    http://shiny.rstudio.com/
#

library(shiny)

# Define server logic required to draw a histogram
shinyServer(function(input, output) {
   
  output$distPlot <- renderPlot({
    
    # generate bins based on input$bins from ui.R
    x    <- faithful[, 2] 
    bins <- seq(min(x), max(x), length.out = input$bins + 1)
    
    # draw the histogram with the specified number of bins
    hist(x, breaks = bins, col = 'darkgray', border = 'white')
    
  })
  
})

Training repository: Go to Chapter 1 > Exercise-1.4.2 > test_shiny

Run either the ui.R or server.R file to see the app in action.


1.5 How does the Shiny app work?

1.5.1 Structure of a Shiny app

A Shiny app has two main parts: the user interface (UI), which is what people see and interact with, and the server, which does the behind-the-scenes work. You can either keep the UI and server in separate files or combine them into one.

The UI and server can share information back and forth in a limited way, using something called “inputs” and “outputs” (we’ll cover this more later!). Besides that, you need to load libraries, data, and other resources separately.

A Shiny app is self-contained, meaning all the data and charts you want to use need to be created within the app itself—you can’t bring in things generated outside of it. However, it can still access objects in your current environment, including libraries (i.e. packages), so be careful with this when building the app.

1.5.2 Loading libraries and data

In this course, we’ll create Shiny apps using two files: one for the user interface (UI) and one for the server.

The UI and server files don’t share things like libraries and data between them. If you need a library in both files, load it separately in each one. This also applies to data, though you’ll rarely need to load data in the UI file.

It’s a good habit to always write the package name with the function, using double colons (like dplyr::filter() instead of just filter()). This is especially helpful when working with Shiny apps, as it helps when you’re developing and hosting the app on multiple servers.

1.6 The UI (User interface)

The UI is what the user sees; inputs, outputs, structures, tabs etc etc. Objects appear on screen in the order they are written in the UI. They are contained within functions within functions, therefore be sure to keep track of all your brackets!

# Load the Shiny package
library(shiny)

# Define the UI
shinyUI(
    fluidPage(
    # Application title
    titlePanel("Hello Shiny!"),
    
    # Sidebar layout with input and output definitions
    sidebarLayout(
        # Sidebar panel for inputs
        sidebarPanel(
            # Input: Slider for the number of bins
            sliderInput("bins",
                        "Number of bins:",
                        min = 1,
                        max = 50,
                        value = 30)
            # if you want to add another input you will need to separate it with a comma
        ),
        
        # Main panel for displaying outputs
        mainPanel(
            # Output: Histogram
            plotOutput("distPlot")
        )
    )
))
  • the whole UI is wrapped in a shinyUI() call (could also use ui <-)
  • the fluidPage() function defines the content of the page
  • the titlePanel() defines the title of the page
  • the sidebarLayout() creates a basic page layout with a sidebar and main panel
  • the sidebarPanel() contains inputs e.g. radio buttons or file inputs
  • the mainPanel() contains outputs e.g. charts or tables separated by commas

1.6.1 The UI; from code to output

1.7 The server

The server is where all the data processing takes place. Nothing in the server is shown directly to the user; it must be sent to the UI to be displayed. All of the server’s code is kept inside one function.

#Define server Logic required to draw a histogram
shinyServer(
  function(input, output) {
    
    output$distPlot <- renderPlot({
        # Generate bins based on input$bins from the slider
        x <- faithful$waiting
        bins <- seq(min(x), max(x), length.out = input$bins + 1)
        
        # Draw the histogram
        hist(x, breaks = bins, col = 'darkgray', border = 'white')
    })
})
  • the whole server is wrapped in a shinyServer() call (could also use server <-)
  • the function() takes the input and output objects as arguments
  • renderPlot() is part of the reactive environment which produces reactive objects and outputs. These do not need to be separated by commas

1.7.1 Input and Outputs

As mentioned before, the server and UI communicate with each other through lists of three types of objects:

  • Input: Objects the user creates by interacting with widgets in the UI, like sliders or dropdowns. They’re often single values or lists of values
  • Output: Objects created in the server that users can see, usually things like charts or tables
  • Session: Not covered in this course, but it’s useful for more advanced apps

1.8 The structure of the UI

1.8.1 The sidebarLayout

The sidebarLayout() is the default layout when you create a new Shiny app. It appearance includes a grey sidebar which is visible throughout the entire app, then the main panel which contains your visualisations (in the previous exercise, the main panel displays the histogram). This layout page is good for small tools with a few key visualisations, all controlled by the same menus.

Example image of the sidebarLayout:

Features of the sidebarLayout:

  • sidebarPanel() creates the sidebar. This contains inputs passed as arguments, separated by commas. The width (1 to 12) and appearance of the sidebar can be set here
# Sidebar layout with input and output definitions
sidebarLayout(
    # Sidebar panel for inputs with width and appearance settings
    sidebarPanel(
        width = 4,  # Sets the width of the sidebar (1 to 12 scale)
        
        # Slider Input
        sliderInput("bins", 
                    "Number of bins:",
                    min = 1, 
                    max = 50, 
                    value = 30), # next input separated by comma
        # Text Input 
        textInput("title", "Chart Title:", value = "My Histogram")
    ),
    
    # Main panel for displaying outputs
    mainPanel(
        plotOutput("distPlot")
    )
)
  • mainPanel() creates a single central panel, which fills the rest of the page. Outputs go inside here e.g. charts or tables separated by commas. You can also add tabs for viewing multiple charts or tables
# Main panel for displaying outputs with tabs
mainPanel(
    # Creates tabs within the main panel
    tabsetPanel(
        tabPanel("Chart", # Tab for chart output
                 plotOutput("distPlot")
                 ),   # separated by comma
        tabPanel("Data Table", # Tab for table output
                 tableOutput("dataTable")
                 )  
    )
)

1.8.2 The navbarPage

navbarPage() is another layout you can use to build the appearance of your app. The layout includes a master navigation bar at the top (or other position) of the screen, with multiple tabs on it. Each individual page can have multiple visualisations and an individual sidebar. This layout is good for more complex tools with sub-components and different datasets.

Example image of a navbarPage:

Example code of a navbarPage:

# Define the UI using navbarPage
navbarPage(
    # Title displayed in the navbar
    title = "Simple Navbar Example",
    # Optional: Unique ID for the navbar
    id = "navbar",
    # Title for the browser window
    windowTitle = "My Shiny App",
    # Optional: Use a dark theme for the navbar (TRUE/FALSE)
    inverse = FALSE,
    # Optional: Position of the navbar (static-top, fixed-top, etc.)
    position = "static-top",           
    # First tab
    tabPanel("Home",                    
        h2("Welcome to the Home Page"),
        p("This is a simple example of a Shiny app using navbarPage.")
        ),
    # Second tab
    tabPanel("Data",                    
        h2("Data Overview"),
        p("Here you can display data tables or other information.")
        ),
    # Third tab
    tabPanel("About",                   
        h2("About This App"),
        p("This app demonstrates the use of navbarPage layout in Shiny.")
        )
)

1.8.3 Tabs

  • tabsetPanel() creates a set of tabs within a main panel of any layout (like fluidPage, navbarPage, etc.). It allows users to switch between different sections of content easily. Providing a tab ID is optional but can be useful for analytics or shortcuts to specific tabs
  • tabPanel() defines each individual tab within the tabsetPanel(). Each tab can contain various UI elements such as text, plots or tables. You must provide a tab name, followed by any UI elements or objects you want to include, separated by commas

Example image of tabs:

Example code of tabs:

# Create the main panel with a tabset panel
    mainPanel(
        # Create a tabset panel
        tabsetPanel(
            id = "tabs",  # Optional: Provide an ID for the tabset
            
            # First tab for a chart
            tabPanel("Chart", plotOutput("irisPlot")  # Output for the plot
            ),
            # Second tab for a table
            tabPanel("Table", tableOutput("irisTable")  # Output for the table
            ),
        )
    )

1.8.4 HTML tags

You can format text in a Shiny app using HTML tags from the Shiny library. These tags help you set header levels, make text bold or italic, add hyperlinks, or create line breaks.

To use them, wrap your text in the corresponding function. For example, h1("This is my text") will display the text as a level 1 header.

You can also add horizontal lines with hr() and line breaks with br(). These functions are used without any text inside them.

Here’s a table of HTML tags you can use to build a Shiny app:

Shiny function Creates
p A paragraph of text
h1 A first level header
h3 A second level header
h3 A third level header
h4 A fourth level header
h5 A fifth level header
h6 A sixth level header
a A hyperlink
br A line break (e.g. a blank line)
div A division of text with a uniform style
span An in-line division of text with a uniform style
code A formatted block of code
img An image
strong Bold text
em Italicised text

1.9 Exercise

15:00

Open Chapter 1 > Exercise-1.9 in the training repository and complete the following tasks:

  1. Change the title of your app to something different
  2. Add some tabs to the main panel of your app! Keep the chart in one tab, and some other text to another tab.
  3. Format the text in your tab so you have different level headers, bold text and some horizontal rules

Bonus

  1. Can you work out how to specify which tab you want to be open when the app starts for the first time?
1.9 Solution


UI

library(shiny)

shinyUI(fluidPage(
  
  ### Question 1. Change the title of the app to something different
  titlePanel("My Shiny App!"),
  
  sidebarLayout(
    sidebarPanel(
      sliderInput("bins",
                  "Number of bins:",
                  min = 1,
                  max = 50,
                  value = 30)
      ),
    ### Question 2. Create two tabs, one called Chart to keep the chart in it
    ### and one called Text to write some text in
    mainPanel(
      tabsetPanel(
        # Chart tab for the plot
        tabPanel("Chart", 
                 plotOutput("distPlot")),
        
        # Text tab with formatted text
        tabPanel("Text", 
                 h2("Welcome to My Shiny App"),  # Header
                 p("This app demonstrates a simple histogram of waiting times for eruptions of the Old Faithful geyser."),
                 p(strong("Adjust the number of bins"), " using the slider to see the effect on the chart.")
        ),
        
        ### Bonus: Set the default tab to "Chart" when the app runs
        id = "tabs", selected = "Chart"
      )
    )
  )
))

Server

library(shiny)

# Define server logic required to draw a histogram
shinyServer(function(input, output) {
  
  output$distPlot <- renderPlot({
    
    # generate bins based on input$bins from ui.R
    x    <- faithful[, 2] 
    bins <- seq(min(x), max(x), length.out = input$bins + 1)
    
    # draw the histogram with the specified number of bins
    hist(x, breaks = bins, col = 'darkgray', border = 'white')
    
  })
  
})

Training repository: Go to Chapter 1 > Exercise-1.9 > Solution

Run either the ui.R or server.R file to see the app in action.


This is a good opportunity to take a 10-minute break away from the computer to refresh your mind, stretch, and reset before continuing onto Chapter 2.