10  Shiny Basics

Published

August 30, 2024

Keywords

R Shiny, Shiny UI, Shiny Server, shiny input elements, shiny output elements

10.1 Introduction

10.1.1 Learning Outcomes

  • Develop basic Shiny Apps using the {shiny} package.
    • Create new apps using the appropriate directory structure.
    • Create various elements in the user interface portion of the app code.
    • Connect User Input IDs and Output IDs to create dynamic elements in the User Interface.

10.1.2 References:

10.1.2.1 Other References

10.2 Overview of Shiny

As a data scientist, you will collaborate with researchers / managers / clients who do not know or have access to R or python.

You may need to allow them to explore the data/analysis themselves, or you want to interactively explore the data together.

Shiny lets you build an app you can run in a browser while R (or Python) runs underneath.

As examples:

  • The Shiny package has eleven built-in examples that each demonstrate how Shiny works, e.g., shiny::runExample("01_hello").
  • Folks showcase some pretty sophisticated interactive data visualizations Shiny Demo Gallery.
  • If you get really good at Shiny, you can make sophisticated websites with it: Better Shoes.

Shiny acts as a wrapper if you will for HTML and CSS as it translates your R code into HTML and CSS sufficient to run an app.

  • You can build apps without learning HTML, CSS, or JavaScript.
  • However, just a little knowledge can help you customize your app to achieve the look and feel you want.

Shiny Apps Support Interactive Exploration and Visualization through three visible operations:

  1. Allow the user to choose inputs.
  2. Execute code to create input objects and manipulate them to produce output objects.
  3. Display static and dynamic output back to the user.

Behind the scenes, Shiny apps employ Reactive Programming to execute multiple tasks:

  • Track object updates and the dependencies among objects.
  • React to input and output object updates.
  • Synchronize across all object updates.

Shiny apps have two main parts as seen in Figure 10.1.

  • The user interface (UI) runs in a web browser.
  • The shiny app server code runs on a machine set up as shiny server (your local computer for now).
Figure 10.1: Shiny apps have two parts - the User interface and the Server.

10.3 Creating Shiny Apps

You write shiny apps in .R files.

  • Basic apps have all the code in one file that should be named app.R.
  • More complicated (or older) apps may use two files: one for the user interface code and one for the server code.
  • Even more complicated apps use multiple files organized as Shiny Modules.
  • It’s a trade off between complexity and maintainability.

We will work with one file for now.

10.3.1 Directory or Folder Structure for {shiny} Apps.

Since the main code must be in a file with a specific name, e.g., app.R, you can’t manage or distinguish apps by file name of the file with your code.

  • Use a distinct top-level folder name to distinguish between apps, e.g. myfirstapp.

The following structure is recommended for your {shiny} apps to allow you to manage them AND to prepare for success:).

Folder - myfirstapp\

  • data-raw\: for original (messy) data and files with code to clean it.
  • data\: for cleaned processed data. Each file in this directory should be a .Rds file (created by readr::write_rds().
  • images\ a sub-directory for any images needed by the app.
  • tests\: for test code and files
  • vignettes\: R Markdown files for your vignette

Recommend using this structure for your final project.

If you want to create your app as a package, you will use a R\: for all files with code needed to run the app - see Chapter 20 Mastering Shiny - Packages and Engineering Production Grade Shiny.

10.3.2 File Naming Conventions

Since all code files for running the app are together at the top level of the folder, a file naming convention can help organize the files for easier maintenance.

The following may be useful for more complicated apps.

  • app_*.R (or app_ui.R and app_server.R) file(s) contain the top level functions defining your user interface and your server function.

  • If your app is large enough to use modules or external functions and utilities suggest the following:

    • fct_*.R files contains the business logic, potentially large functions. They are the backbone of the application and may not be specific to a given module.
    • mod_*.R files contain a unique module (if you use them). Many Shiny apps contain a series of tabs, or at least a tab-like pattern, so number them according to their step in the application.
      • Tabs are almost always named in the user interface, so use the tab-name as the file name.
      • For example, if the first tab is called “Import”, name its file mod_01_import.R.
    • utils_*.R files contain small helper functions.
      • For example, you might have a not_na(), which is not_na() <- Negate(is.na), a not_null(), or other small tools used application-wide.

10.3.3 Structure of a {Shiny} App.

Every app.R File has the same basic structure with three main sections followed by the line with the function call to create the app object.

  1. Business Logic Section
  2. User Interface Section
  3. Server Section
library(shiny)
# 1. Business Logic
# This code only needs to run once - when the App opens
# It does not have to react to any user inputs

# 2. Define the UI for the app to get inputs and display outputs
ui <- fluidPage(
  ## Stuff for defining user-interface
  ## e.g. Title,
  ##      Table of contents
  ##      Location of inputs (sliders and buttons)
)

# 3. Define server logic to manipulate input objects, data,
#  to produce output objects
server <- function(input, output) {
  ## Stuff for running R code
  ## e.g. Run a linear model.
  ##      Manipulate a data frame
  ##      Make a plot.
}

# Create the application
shinyApp(ui = ui, server = server)

The sections of the app.R File Handle Different Tasks

  1. Business Logic
  • Implements code that does not depend upon, or need to, react to user input or later analysis, e.g.,
    • Load the shiny library (and other libraries),
    • Load pre-determined data files,
    • Manipulate the pre-loaded data to tidy, clean, and create the information you need for the user (if not already done and saved in the data folder as a .Rds file).
    • Define functions to simplify your later code.
    • All of this code should be able to be run in a normal .R script file to debug or potentially operate separately.
  1. User Interface
  • Defines the user interface using a page layout function such as the fluidPage() function.
    • The user interface defines things like page layout (title, headers, table of contents), and sources of input (number sliders and buttons).
  • Saves the output of fluidPage() to a variable (called ui above).
  1. Server Logic
  • The code to do everything else required to respond to user inputs and create new output values.
  • Creates the server logic as a single R function e.g., server <- function(){...}.
    • This is just regular R code wrapped in some special Shiny functions.
    • The shiny functions allow access to the input objects in the shiny app environment.
    • You can use input to manipulate data frames, fit statistical models, or make plots, etc..
  1. Last Line
  • Calls the shinyApp() function, with the names of the outputs from fluid_page() and the server function.
    • This is the same for all app.R files.
  • Note: Shiny has several UI Page Layout Functions besides fluidPage()

10.3.4 Your First Shiny App

10.3.4.1 Use RStudio to Create a Directory and app.R File for a new Shiny App

You can create a basic Shiny app in RStudio by clicking File/New "Shiny Web App...:

Create a new Shiny app, step 1.

A pop-up window will ask for the name of your new shiny app and the directory where you want it created.

  • The name needs to follow the R conventions for variable naming.
  • The name cannot start with a number or use special characters

RStudio will create a new folder under the directory you specified with the name you entered for your App.

Let’s call our shiny app my_first_app.

Create a new Shiny app, step 2.

RStudio provides a complete starter template file app.R under the new directory.

  • It should look like this:
#
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
#    http://shiny.rstudio.com/
#

library(shiny)

# Define UI for application that draws a histogram
ui <- 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")
    )
  )
)

# Define server logic required to draw a histogram
server <- 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")
  })
}

# Run the application
shinyApp(ui = ui, server = server)

10.3.4.2 Running your App

With this app.R file open, click on "Run App" at the top right of your Source panel.

  • Notice your console is busy.

  • You cannot run R code in the console while the shiny app is running.

  • Your console should say something like “Listening on http://127.0.0.1:6342”, but the url will be different.

    • You can put the URL into your web browser to see the running Shiny App.
    • Don’t worry, it’s still on your computer. The URL just points to a location on your machine.

Sample App.

Exit out of the Shiny App by:

  1. Clicking the stop sign on your Viewer Tab, or,
  2. Hitting CTRL + C while you are in the console, or,
  3. Closing the Shiny App window.

10.4 User Interface Input Elements

The user interface is defined by everything within the fluidPage() section.

  • fluidpage() has a simple default layout with a sidebar for inputs and a large main area for output.

Shiny provides a number of page layout functions besides fluidpage().

  • Each serves as a template for creating common layouts for user interface elements to collect user inputs.
  • These include: fillPage(), fixedPage(), flowLayout(), navbarPage(), sidebarLayout(), splitLayout(), and verticalLayout().

There can be hierarchical (nested) layouts within each of these to enable very rich and complex UIs.

10.4.1 Let’s Create a Bare Bones Template

Create a new shiny app called “tryouts”.

To make it bare bones:

  • Delete all the template code.
  • Start typing shinyapp in the file and select shinyapp {snippet} from the auto-complete list to generate the following.
library(shiny)

ui <- fluidPage()

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

}

shinyApp(ui, server)
  • Recommend using the above as a template for all future Shiny apps.
  • It is the minimal amount of coded needed to start a Shiny app.

Running this app should just produce a blank HTML page in the browser.

10.4.2 User Input functions

Populate fluidPage(), with calls to shiny functions for input elements with each call separated by a comma.

Four common arguments to fluidpage() are calls to functions for different types of UI input elements:

  • textInput()
  • numericInput()
  • sliderInput()
  • selectInput()

Each of these input functions requires at least three arguments:

  • inputId: The name of the input object. The server() function will access user inputs through the inputId, so it needs to follow the conventions for variable naming.
    • character strings with no spaces and can’t start with a number
  • label: The name displayed on the web-app to the user.
  • value: A default input.

Each input function may also take many more arguments specific to that type of input.

  • The page layout function creates an R list of the inputIds so the server function can find them.
Tip

A difficult error to debug is when an inputId in the server side has a typo in it.

The server function just ignores the unknown ID but does not stop or generate an error message.

If you are having trouble with an app not doing what you expect and there is no error message, check the id name is spelled the same in the UI section and the server section.

10.4.3 Get Text Inputs with text_input(), passwordInput() and textAreaInput()

textInput() accepts one line of text from the user.

passwordInput() accepts one line of text which is not displayed on the screen as it is entered. (NOTE: This is not a secure way to collect passwords by itself.)

textAreaInput() accepts multiple lines of text.

Add these elements to your tryouts app:

library(shiny)

ui <- fluidPage(
  textInput("name", "What's your name?"),
  passwordInput("password", "What's your password?"),
  textAreaInput("story", "Tell me about yourself")
)

server <- function(input, output) {

}

shinyApp(ui = ui, server = server)

Running the app, you should get something like this:

Text input options.

Later on, we’ll cover how to access these inputs in the server function().

  1. Change the width and height arguments in textAreaInput(). What does it do?

It changes the default width and height of the text box.

  1. Change the value argument in textInput(). What does it do?
It changes the default text in the text box.

10.4.4 Get Numeric Inputs with numericInput() and sliderInput()

numericInput() creates a text box that only accepts numeric values.

sliderInput() creates a numeric slider.

  • Giving the value argument one number results in a one-sided slider.
  • Giving the value argument a vector of two numbers results in a two-sided slider (min, max).
library(shiny)

ui <- fluidPage(
  numericInput("num", "Number one", value = 0, min = 0, max = 100),
  sliderInput("num2", "Number two", value = 50, min = 0, max = 100),
  sliderInput("rng", "Range", value = c(10, 20), min = 0, max = 100)
)

server <- function(input, output) {

}

shinyApp(ui = ui, server = server)

Running the app, you should get something like this:

Slider input options.
  • Only use sliders for small ranges where the exact number is not important.

What does the animate option do when you set it to TRUE?

If you push play, the slider slowly slides up until it reaches the maximum value.

10.4.5 Get Date Inputs with dateInput() and dateRangeInput()

Use dateInput() to collect a single date.

Use dateRangeInput() to collect two dates.

library(shiny)

ui <- fluidPage(
  dateInput("dob", "When were you born?"),
  dateRangeInput("holiday", "When do you want to go on vacation next?")
)

server <- function(input, output) {

}

shinyApp(ui = ui, server = server)

Running the app, you should get something like this:

Date input options

In dateInput(), try to disable the selection of Sundays, Mondays, Fridays, and Saturdays.

Show code
dateInput("dob", "When were you born?", daysofweekdisabled = c(0, 1, 5, 6))

10.4.6 Get Multiple Choice Input with selectInput(), radioButtons(), or checkboxGroupInput()

Use selectInput() to provide the user with a drop-down menu.

Use radioButtons() to have a multiple choice button selection where only one selection is possible.

Use checkboxGroupInput() to have a multiple choice button selection where multiple selections are possible.

library(shiny)

weekdays <- c("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat")
ui <- fluidPage(
  selectInput("state", "Where do you live?",
    choices = state.name
  ),
  radioButtons("weekday", "What's your favorite day of the week?",
    choices = weekdays
  ),
  checkboxGroupInput("weekday2", "What days do you work?",
    choices = weekdays
  )
)

server <- function(input, output) {

}

shinyApp(ui = ui, server = server)

Running the app, you should get something like:

Choice input options: Dropdown, Radio Buttons, and Check Box Group.

What does multiple = TRUE do in in selectInput()?

It allows for multiple inputs.

10.4.7 Select Columns of a Data Frame with varSelectInput()

varSelectInput() allows a user to select a variable (column) from a data frame already loaded in the environment.

  • We will do this a lot!
library(shiny)

ui <- fluidPage(
  varSelectInput("carcol", "Which Column?", data = mtcars)
)

server <- function(input, output) {

}

shinyApp(ui = ui, server = server)

Running the app, you should get something like:

Data frame input variable selection.

What does the selected argument do? Change it.

It changes the default column.

Show code
varSelectInput("carcol", "Which Column?", data = mtcars, selected = "disp")

varSelectInput() gives the server function access to a name object.

  • You may not have worked with these as programming objects.
  • You can read about them using help(name).
Important

input$var_name is a name (aka symbol) object in the Shiny environment.

When input$var_name is the ID for a varSelectInput() (selecting a data frame variable) you have to treat it differently in tidyverse and Base R functions.

If you want to use the input$var_name as a variable name in a tidyverse function, you need to prefix the input$ with the injection operator (aka bang-bang operator) !! from the {rlang} package.

  • The !! in !!input$var_name modifies the environment “name” object ( a character value) into a data frame variable name before it is evaluated in the tidyverse function.

To use the input$var_name in a base R function (that does not use data masking), use it inside the [[]] operator as in [[input$var_name]].

The !! is also known as the “Unquote” operator in that it removes the “quotes” from a “quoted” function or expression.

  • In this case, we are using an “indirect reference” expression. The input$var variable contains a value which is also a variable name from the data frame.
  • This is part of R’s approach to meta-programming discussed in Advanced R Chapter 19
  • Unquoting allows you to selectively evaluate parts of the expression that would otherwise be quoted.
  • Use !! to unquote a single argument in a function call.
  • !! takes a single expression, evaluates it, and fits it into the evaluation of the overarching function (the abstract syntax tree (AST)).

We’ll do an example of this in the “Putting Inputs and Outputs Together” section, Section 10.6.

10.4.8 Get Binary (TRUE/FALSE) Inputs with checkboxInput()

Use checkboxInput() to get a TRUE/FALSE or Yes/No answer.

library(shiny)

ui <- fluidPage(
  checkboxInput("milk", "Got Milk?")
)

server <- function(input, output) {

}

shinyApp(ui = ui, server = server)

Running the app, you should get something like this:

Check Box input option.

What are the possible values for the value argument? Change it.

It can be either TRUE (so the box is checked) or FALSE (so the box is unchecked).

Show code
checkboxInput("milk", "Got Milk?", value = TRUE)

10.4.9 Get File Names for Inputs with fileInput()

fileInput() allows users to input one or more file names.

library(shiny)

ui <- fluidPage(
  fileInput("filename", "Which File?", multiple = FALSE)
)

server <- function(input, output) {

}

shinyApp(ui = ui, server = server)

Running the app, you should get something like this:

File Selection input option.

What does the buttonLabel argument do? Change it.

It changes the name on the button from “Browse”.

Show code
fileInput("upload_file", "Please Select a File", buttonLabel = "Look Up")

10.5 User Interface Output Elements

Output functions create placeholders for the values of the objects created in the server function() (like plots and tables) to be displayed as output in the User Interface.

Each output function’s first argument is its outputId, just like the input elements.

  • The server function() can access this element as an element of the output list.
  • For example, if the first argument is outputId="plot", the server function can insert a plot into output$plot.

Each UI output function has an associated render function in the server function().

  • A render function first uses the code in the R expression (that you wrote inside { }) to create an R output object, e.g., text, or a table, or a plot. The function then “renders” the object into HTML code that will generate the output produced by the expression and passes it to the ui() function which will pass the HTML to the browser to display the output.
  • As an example, if you create a renderPlot() expression to create a plot object, say using ggplot2(), the renderPlot() function will generate the HTML for displaying the plot and pass to the ui() function to put in the output$plot placeholder in the user interface.

10.5.1 Display Text Output with textOutput() or verbatimTextOutput()

Use textOutput() to display text.

Use verbatimTextOutput() to display code.

Create text objects in the server function() with either renderText() or renderPrint().

  • renderText() will display text returned by code. Functions can only return one thing.
  • renderPrint() will display text printed by code. Functions can print multiple things.
  • Use verbatimTextOuput() with renderPrint() to print results from outputs of t.test() or lm().
library(shiny)

ui <- fluidPage(
  textOutput("text"),
  verbatimTextOutput("code")
)

server <- function(input, output, session) {
  output$text <- renderText({
    "Hello World!"
  })

  output$code <- renderPrint({
    summary(c(1, 2, 3, 4))
  })
}

shinyApp(ui = ui, server = server)

Running the app, you should get something like this:

Text output options.

Change the outputId from “text” to something else. Make sure the Shiny App still works.

Show code
library(shiny)

ui <- fluidPage(
  textOutput("text2"),
  verbatimTextOutput("code")
)

server <- function(input, output, session) {
  output$text2 <- renderText({
    "Hello World!"
  })

  output$code <- renderPrint({
    summary(c(1, 2, 3, 4))
  })
}

shinyApp(ui = ui, server = server)

10.5.2 Display Output Tables with tableOutput() or dataTableOutput()

tableOutput() prints an entire table created in the server by renderTable().

  • Should only be used for small tables.
library(shiny)

ui <- fluidPage(
  tableOutput("static")
)

server <- function(input, output, session) {
  output$static <- renderTable({
    head(mtcars)
  })
}

shinyApp(ui = ui, server = server)

Running the app, you should get something like this:

Table output option.

dataTableOutput() displays a dynamic table created in the server by renderDT().

Important

Shiny used to use the renderDataTable() function to create dynamic interactive tables. However that has been superseded and replaced by renderDT().

The renderDT() function is from the {DT} package (Data Tables package) which provides access to the JavaScript library DataTables. This package has more options for customizing your interactive tables.

You will need to install the package using the console and for each app, load the {DT} package in the business logic section with library(DT).

library(shiny)
library(DT)

ui <- fluidPage(
  dataTableOutput("dynamic")
)

server <- function(input, output, session) {
  output$dynamic <- renderDT({
    mtcars
  })
}

shinyApp(ui = ui, server = server)

Running the app, you should get something like this:

Data Table output option

You can change the appearance of dataTableOutput() by passing arguments as a list to the options argument in renderDT().

```{r}
#| eval: false
renderDT(
  {
    mtcars
  },
  options = list(pageLength = 5)
)
```

N

10.5.3 Output Plots to the UI with plotOutput()

plotOutput() displays plots created by renderPlot() in the server() function.

  • Note we need to load {ggplot2} as well as {shiny}.
library(shiny)
library(ggplot2)

ui <- fluidPage(
  plotOutput("plot", width = "60%")
)

server <- function(input, output, session) {
  output$plot <- renderPlot({
    ggplot(mpg, aes(x = displ, y = hwy)) +
      geom_point() +
      theme_bw() +
      xlab("Displacement") +
      ylab("Highway MPG")
  })
}

shinyApp(ui = ui, server = server)

Running the app, you should get something like this:

Plot output option.

Change the height and width of the plot by changing the argument values in renderPlot().

Change the height and width of the plot by changing the argument values in plotOutput().

Show code
```{r}
#| eval: false
#| code-fold: true
renderPlot(
  {
    ggplot(mpg, aes(x = displ, y = hwy)) +
      geom_point() +
      theme_bw() +
      xlab("Displacement") +
      ylab("Highway MPG")
  },
  width = 300,
  height = 250
)
# or
plotOutput("plot", width = "300px", height = "250px")
# or
plotOutput("plot", width = "40%")
```

10.6 Putting Inputs and Outputs Together

Let’s build a simple shiny app that lets the user choose two variables from mtcars and then plots them.

  • Start with the app template and make it bare bones.
  • Load {shiny} and {ggplot2} at the beginning of the app, in the business logic section, before the ui <- fluidpage(...).
  • Add the UI inputs and outputs.
  • Add the server code to render the plot and assign to an output object.
library(shiny)
library(ggplot2)

ui <- fluidPage(
  varSelectInput("var1", "Variable 1", data = mtcars),
  varSelectInput("var2", "Variable 2", data = mtcars),
  plotOutput("plot")
)

server <- function(input, output) {
  output$plot <- renderPlot({
    ggplot(mtcars, aes(x = !!(input$var1), y = !!input$var2)) +
      geom_point()
  })
}

shinyApp(ui = ui, server = server)

Running the app, you should get something like this:

Connecting data frame input variable selections with Plot output.

Notice how in the server function(),

  • We accessed the inputs via the input variable.

    • These inputs must be used inside a render function to obtain reactivity.
  • We set the outputs to the output() variable.

  • Notice the use of the unquoting function !! in front of input$var1 and input$var2.

    • You have to do this to use data frame variable names in functions from the tidyverse ({ggplot}, {dplyr}, {tidyr}, etc) (but not base R where variable names are evaluated differently).

Let the user choose whether they want “red” points or “blue” points.

library(shiny)
library(ggplot2)

ui <- fluidPage(
  varSelectInput("var1", "Variable 1", data = mtcars),
  varSelectInput("var2", "Variable 2", data = mtcars),
  radioButtons("color", "What Color?",
    choiceNames = c("Red", "Blue"),
    choiceValues = c("red", "blue")
  ),
  plotOutput("plot")
)

server <- function(input, output) {
  output$plot <- renderPlot({
    ggplot(mtcars, aes(x = !!input$var1, y = !!input$var2)) +
      geom_point(color = input$color) #- Notice the non-use of `!!`
  })
}

shinyApp(ui = ui, server = server)

Running the app, you should get something like this:

Connecting input point color to output plot.

10.6.1 Identifying varSelectInput() Data Frame Variables in Base R Functions

The tidyverse uses a special way of creating environments which allows it to use “tidy evaluation” when evaluating tidyverse functions.

  • This has the benefit of speeding up interactive analysis of data as you do not have to fully specify the variable names inside some tidyverse functions that use “data masking”.
  • However, it has the downside of making it hard to program with tidyverse functions as you have to be careful about how you identify the variables indirectly.
  • Shiny makes things a bit more complicated as the objects that get passed between input and output are not “normal” variables.

You must identify the input variables differently in tidyverse functions than in Base R functions.

The following example uses the Base R (non-tidyverse) functions is.factor() and lm() in the server() function.

  • Note the use of the [[]] subset operator which matches the character “name object” to the names of the variable.
  • See Chapter 12 of Mastering Shiny for a discussion of data variables and environment variables.
  • See help for [[ as well.
library(shiny)

ui <- fluidPage(
  varSelectInput("y", "y", data = iris, selected = "Sepal.Length"),
  varSelectInput("x", "x", data = iris, selected = "Sepal.Width"),
  checkboxInput("show_str", "Show str(X) if X is factor?"),
  verbatimTextOutput("test_str"),
  verbatimTextOutput("lm_results")
)

server <- function(input, output, session) {
  output$test_str <- renderPrint({
    if (input$show_str) {
      if (is.factor(iris[[input$x]])) {
        #   str(input$x) # shows without [[  ]]
        str(iris[[input$x]])
      } # if factor
    } # end if show_str
  }) # end render Print

  output$lm_results <- renderPrint({
    lmout <- lm(iris[[input$y]] ~ iris[[input$x]], data = iris)
    print(summary(lmout))
  })
}

shinyApp(ui, server)

10.6.2 Variations of Text and Data Output

Given the different types of text elements, it can be confusing to ensure the input and output objects align with respect to formats.

Consider the following examples of alignment in the code and the results in Figure 10.2.

Notice what happens when you use broom::tidy() to convert the list output from the analysis of variance function aov to a data frame.

library(shiny)
library(broom)

data("mtcars")
aout <- aov(mpg ~ as.factor(cyl), data = mtcars)

ui <- fluidPage(
  textOutput("character_text"),
  textOutput("character_variable"),
  verbatimTextOutput("code"),
  tableOutput("my_tidy_table"),
  dataTableOutput("my_data_table")
)

server <- function(input, output, session) {
  output$character_text <- renderText({
    glue::glue("These are the names of the {length(mtcars)} variables in `mtcars`")
  })

  output$character_variable <- renderText({
    names(mtcars)
  })

  output$code <- renderPrint({
    aout
  })

  output$my_tidy_table <- renderTable({
    tidy(aout)
  })

  output$my_data_table <- renderDataTable({
    tidy(aout)
  })
}

shinyApp(ui = ui, server = server)
Figure 10.2: Examples of different ways to align text inputs and outputs.

10.6.3 Adjusting Font Size for Plot Axis Titles and Axis Text

Shiny can default to plot axis labels that are too small to read.

The following code is one example of creating a theme as seen in Figure 10.3.

  • Note the theme object is created in the business logic section, as a variable you can add to multiple plots instead of typing the same theme code over and over.

We will cover much more about themes later.

library(shiny)
library(ggplot2)

my_theme <- theme(
  axis.title.x = element_text(size = rel(1.5), color = "red"),
  axis.title.y = element_text(size = rel(2.5), color = "blue"),
  axis.text.x = element_text(size = 14, face = "bold.italic"),
  axis.text.y = element_text(size = 16, angle = 180)
)

ui <- fluidPage(
  varSelectInput("var1", "Variable 1", data = mtcars),
  varSelectInput("var2", "Variable 2", data = mtcars),
  plotOutput("plot")
)

server <- function(input, output) {
  output$plot <- renderPlot({
    ggplot(mtcars, aes(x = !!(input$var1), y = !!input$var2)) +
      geom_point() +
      my_theme
  })
}

shinyApp(ui = ui, server = server)
Figure 10.3: Results of Adjusting Axis Labels and Axis Text using a theme (albeit not the best theme).