Shiny is a new package from RStudio that makes it incredibly easy to build interactive web applications with R.
For an introduction and live examples, visit the Shiny homepage.
Shiny is available on CRAN, so you can install it in the usual way from your R console:
install.packages("shiny")
This tutorial covers the basics of Shiny and provides detailed examples of using much of its capabilities. Click the Next button to get started and say hello to Shiny!
The Hello Shiny example is a simple application that generates a random distribution with a configurable number of observations and then plots it. To run the example, type:
> library(shiny)
> runExample("01_hello")
Shiny applications have two components: a user-interface definition and a server script. The source code for both of these components is listed below.
In subsequent sections of the tutorial we’ll break down all of the code in detail and explain the use of “reactive” expressions for generating output. For now, though, just try playing with the sample application and reviewing the source code to get an initial feel for things. Be sure to read the comments carefully.
The user interface is defined in a source file named ui.R:
library(shiny)
# Define UI for application that plots random distributions
shinyUI(pageWithSidebar(
# Application title
headerPanel("Hello Shiny!"),
# Sidebar with a slider input for number of observations
sidebarPanel(
sliderInput("obs",
"Number of observations:",
min = 0,
max = 1000,
value = 500)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
))
The server-side of the application is shown below. At one level, it’s very simple–a random distribution with the requested number of observations is generated, and then plotted as a histogram. However, you’ll also notice that the function which returns the plot is wrapped in a call to renderPlot
. The comment above the function explains a bit about this, but if you find it confusing, don’t worry–we’ll cover this concept in much more detail soon.
library(shiny)
# Define server logic required to generate and plot a random distribution
shinyServer(function(input, output) {
# Expression that generates a plot of the distribution. The expression
# is wrapped in a call to renderPlot to indicate that:
#
# 1) It is "reactive" and therefore should be automatically
# re-executed when inputs change
# 2) Its output type is a plot
#
output$distPlot <- renderPlot({
# generate an rnorm distribution and plot it
dist <- rnorm(input$obs)
hist(dist)
})
})
The next example will show the use of more input controls, as well as the use of reactive functions to generate textual output.
The Shiny Text application demonstrates printing R objects directly, as well as displaying data frames using HTML tables. To run the example, type:
> library(shiny)
> runExample("02_text")
The first example had a single numeric input specified using a slider and a single plot output. This example has a bit more going on: two inputs and two types of textual output.
If you try changing the number of observations to another value, you’ll see a demonstration of one of the most important attributes of Shiny applications: inputs and outputs are connected together “live” and changes are propagated immediately (like a spreadsheet). In this case, rather than the entire page being reloaded, just the table view is updated when the number of observations change.
Here is the user interface definition for the application. Notice in particular that the sidebarPanel
and mainPanel
functions are now called with two arguments (corresponding to the two inputs and two outputs displayed):
library(shiny)
# Define UI for dataset viewer application
shinyUI(pageWithSidebar(
# Application title
headerPanel("Shiny Text"),
# Sidebar with controls to select a dataset and specify the number
# of observations to view
sidebarPanel(
selectInput("dataset", "Choose a dataset:",
choices = c("rock", "pressure", "cars")),
numericInput("obs", "Number of observations to view:", 10)
),
# Show a summary of the dataset and an HTML table with the requested
# number of observations
mainPanel(
verbatimTextOutput("summary"),
tableOutput("view")
)
))
The server side of the application has also gotten a bit more complicated. Now we create:
renderPrint
and renderTable
) that return the output$summary
and output$view
valuesThese expressions work similarly to the renderPlot
expression used in the first example: by declaring a rendering expression you tell Shiny that it should only be executed when its dependencies change. In this case that’s either one of the user input values (input$dataset
or input$n
).
library(shiny)
library(datasets)
# Define server logic required to summarize and view the selected dataset
shinyServer(function(input, output) {
# Return the requested dataset
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
# Generate a summary of the dataset
output$summary <- renderPrint({
dataset <- datasetInput()
summary(dataset)
})
# Show the first "n" observations
output$view <- renderTable({
head(datasetInput(), n = input$obs)
})
})
We’ve introduced more use of reactive expressions but haven’t really explained how they work yet. The next example will start with this one as a baseline and expand significantly on how reactive expressions work in Shiny.
The Reactivity application is very similar to Hello Text, but goes into much more detail about reactive programming concepts. To run the example, type:
> library(shiny)
> runExample("03_reactivity")
The previous examples have given you a good idea of what the code for Shiny applications looks like. We’ve explained a bit about reactivity, but mostly glossed over the details. In this section, we’ll explore these concepts more deeply. If you want to dive in and learn about the details, see the Understanding Reactivity section, starting with Reactivity Overview.
The Shiny web framework is fundamentally about making it easy to wire up input values from a web page, making them easily available to you in R, and have the results of your R code be written as output values back out to the web page.
input values => R code => output values
Since Shiny web apps are interactive, the input values can change at any time, and the output values need to be updated immediately to reflect those changes.
Shiny comes with a reactive programming library that you will use to structure your application logic. By using this library, changing input values will naturally cause the right parts of your R code to be reexecuted, which will in turn cause any changed outputs to be updated.
Reactive programming is a coding style that starts with reactive values–values that change over time, or in response to the user–and builds on top of them with reactive expressions–expressions that access reactive values and execute other reactive expressions.
What’s interesting about reactive expressions is that whenever they execute, they automatically keep track of what reactive values they read and what reactive expressions they invoked. If those “dependencies” become out of date, then they know that their own return value has also become out of date. Because of this dependency tracking, changing a reactive value will automatically instruct all reactive expressions that directly or indirectly depended on that value to re-execute.
The most common way you’ll encounter reactive values in Shiny is using the input
object. The input
object, which is passed to your shinyServer
function, lets you access the web page’s user input fields using a list-like syntax. Code-wise, it looks like you’re grabbing a value from a list or data frame, but you’re actually reading a reactive value. No need to write code to monitor when inputs change–just write reactive expression that read the inputs they need, and let Shiny take care of knowing when to call them.
It’s simple to create reactive expression: just pass a normal expression into reactive
. In this application, an example of that is the expression that returns an R data frame based on the selection the user made in the input form:
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
To turn reactive values into outputs that can viewed on the web page, we assigned them to the output
object (also passed to the shinyServer
function). Here is an example of an assignment to an output that depends on both the datasetInput
reactive expression we just defined, as well as input$obs
:
output$view <- renderTable({
head(datasetInput(), n = input$obs)
})
This expression will be re-executed (and its output re-rendered in the browser) whenever either the datasetInput
or input$obs
value changes.
Now that we’ve taken a deeper loop at some of the core concepts, let’s revisit the source code and try to understand what’s going on in more depth. The user interface definition has been updated to include a text-input field that defines a caption. Other than that it’s very similar to the previous example:
library(shiny)
# Define UI for dataset viewer application
shinyUI(pageWithSidebar(
# Application title
headerPanel("Reactivity"),
# Sidebar with controls to provide a caption, select a dataset, and
# specify the number of observations to view. Note that changes made
# to the caption in the textInput control are updated in the output
# area immediately as you type
sidebarPanel(
textInput("caption", "Caption:", "Data Summary"),
selectInput("dataset", "Choose a dataset:",
choices = c("rock", "pressure", "cars")),
numericInput("obs", "Number of observations to view:", 10)
),
# Show the caption, a summary of the dataset and an HTML table with
# the requested number of observations
mainPanel(
h3(textOutput("caption")),
verbatimTextOutput("summary"),
tableOutput("view")
)
))
The server script declares the datasetInput
reactive expression as well as three reactive output values. There are detailed comments for each definition that describe how it works within the reactive system:
library(shiny)
library(datasets)
# Define server logic required to summarize and view the selected dataset
shinyServer(function(input, output) {
# By declaring datasetInput as a reactive expression we ensure that:
#
# 1) It is only called when the inputs it depends on changes
# 2) The computation and result are shared by all the callers (it
# only executes a single time)
# 3) When the inputs change and the expression is re-executed, the
# new result is compared to the previous result; if the two are
# identical, then the callers are not notified
#
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
# The output$caption is computed based on a reactive expression that
# returns input$caption. When the user changes the "caption" field:
#
# 1) This expression is automatically called to recompute the output
# 2) The new caption is pushed back to the browser for re-display
#
# Note that because the data-oriented reactive expression below don't
# depend on input$caption, those expression are NOT called when
# input$caption changes.
output$caption <- renderText({
input$caption
})
# The output$summary depends on the datasetInput reactive expression,
# so will be re-executed whenever datasetInput is re-executed
# (i.e. whenever the input$dataset changes)
output$summary <- renderPrint({
dataset <- datasetInput()
summary(dataset)
})
# The output$view depends on both the databaseInput reactive expression
# and input$obs, so will be re-executed whenever input$dataset or
# input$obs is changed.
output$view <- renderTable({
head(datasetInput(), n = input$obs)
})
})
We’ve reviewed a lot code and covered a lot of conceptual ground in the first three examples. The next section focuses on the mechanics of building a Shiny application from the ground up and also covers tips on how to run and debug Shiny applications.
Let’s walk through the steps of building a simple Shiny application. A Shiny application is simply a directory containing a user-interface definition, a server script, and any additional data, scripts, or other resources required to support the application.
To get started building the application, create a new empty directory wherever you’d like, then create empty ui.R
and server.R
files within in. For purposes of illustration we’ll assume you’ve chosen to create the application at ~/shinyapp:
~/shinyapp
|-- ui.R
|-- server.R
Now we’ll add the minimal code required in each source file. We’ll first define the user interface by calling the function pageWithSidebar
and passing it’s result to the shinyUI
function:
library(shiny)
# Define UI for miles per gallon application
shinyUI(pageWithSidebar(
# Application title
headerPanel("Miles Per Gallon"),
sidebarPanel(),
mainPanel()
))
The three functions headerPanel
, sidebarPanel
, and mainPanel
define the various regions of the user-interface. The application will be called “Miles Per Gallon” so we specify that as the title when we create the header panel. The other panels are empty for now.
Now let’s define a skeletal server implementation. To do this we call shinyServer
and pass it a function that accepts two parameters: input
and output
:
library(shiny)
# Define server logic required to plot various variables against mpg
shinyServer(function(input, output) {
})
Our server function is empty for now but later we’ll use it to define the relationship between our inputs and outputs.
We’ve now created the most minimal possible Shiny application. You can run the application by calling the runApp
function as follows:
> library(shiny)
> runApp("~/shinyapp")
If everything is working correctly you’ll see the application appear in your browser looking something like this:
We now have a running Shiny application however it doesn’t do much yet. In the next section we’ll complete the application by specifying the user-interface and implementing the server script.
The application we’ll be building uses the mtcars data from the R datasets package, and allows users to see a box-plot that explores the relationship between miles-per-gallon (MPG) and three other variables (Cylinders, Transmission, and Gears).
We want to provide a way to select which variable to plot MPG against as well as provide an option to include or exclude outliers from the plot. To do this we’ll add two elements to the sidebar, a selectInput
to specify the variable and a checkboxInput
to control display of outliers. Our user-interface definition looks like this after adding these elements:
library(shiny)
# Define UI for miles per gallon application
shinyUI(pageWithSidebar(
# Application title
headerPanel("Miles Per Gallon"),
# Sidebar with controls to select the variable to plot against mpg
# and to specify whether outliers should be included
sidebarPanel(
selectInput("variable", "Variable:",
list("Cylinders" = "cyl",
"Transmission" = "am",
"Gears" = "gear")),
checkboxInput("outliers", "Show outliers", FALSE)
),
mainPanel()
))
If you run the application again after making these changes you’ll see the two user-inputs we defined displayed within the sidebar:
Next we need to define the server-side of the application which will accept inputs and compute outputs. Our server.R file is shown below, and illustrates some important concepts:
input
object and generating output by assigning to slots on the output
object.The basic task of a Shiny server script is to define the relationship between inputs and outputs. Our script does this by accessing inputs to perform computations and by assigning reactive expressions to output slots.
Here is the source code for the full server script (the inline comments explain the implementation technqiues in more detail):
library(shiny)
library(datasets)
# We tweak the "am" field to have nicer factor labels. Since this doesn't
# rely on any user inputs we can do this once at startup and then use the
# value throughout the lifetime of the application
mpgData <- mtcars
mpgData$am <- factor(mpgData$am, labels = c("Automatic", "Manual"))
# Define server logic required to plot various variables against mpg
shinyServer(function(input, output) {
# Compute the forumla text in a reactive expression since it is
# shared by the output$caption and output$mpgPlot expressions
formulaText <- reactive({
paste("mpg ~", input$variable)
})
# Return the formula text for printing as a caption
output$caption <- renderText({
formulaText()
})
# Generate a plot of the requested variable against mpg and only
# include outliers if requested
output$mpgPlot <- renderPlot({
boxplot(as.formula(formulaText()),
data = mpgData,
outline = input$outliers)
})
})
The use of renderText
and renderPlot
to generate output (rather than just assigning values directly) is what makes the application reactive. These reactive wrappers return special expressions that are only re-executed when their dependencies change. This behavior is what enables Shiny to automatically update output whenever input changes.
The server script assigned two output values: output$caption
and output$mpgPlot
. To update our user interface to display the output we need to add some elements to the main UI panel.
In the updated user-interface definition below you can see that we’ve added the caption as an h3 element and filled in it’s value using the textOutput
function, and also rendered the plot by calling the plotOutput
function:
library(shiny)
# Define UI for miles per gallon application
shinyUI(pageWithSidebar(
# Application title
headerPanel("Miles Per Gallon"),
# Sidebar with controls to select the variable to plot against mpg
# and to specify whether outliers should be included
sidebarPanel(
selectInput("variable", "Variable:",
list("Cylinders" = "cyl",
"Transmission" = "am",
"Gears" = "gear")),
checkboxInput("outliers", "Show outliers", FALSE)
),
# Show the caption and plot of the requested variable against mpg
mainPanel(
h3(textOutput("caption")),
plotOutput("mpgPlot")
)
))
Running the application now shows it in its final form including inputs and dynamically updating outputs:
Now that we’ve got a simple application running we’ll probably want to make some changes. The next topic covers the basic cycle of editing, running, and debugging Shiny applications.
Throughout the tutorial you’ve been calling runApp
to run the example applications. This function starts the application and opens up your default web browser to view it. The call is blocking, meaning that it prevents traditional interaction with the console while the applciation is running.
To stop the application you simply interupt R – you can do this by pressing the Escape key in all R front ends as well as by clicking the stop button if your R environment provides one.
If you don’t want to block access to the console while running your Shiny application you can also run it in a separate process. You can do this by opening a terminal or console window and executing the following:
R -e "shiny::runApp('~/shinyapp')"
By default runApp
starts the application on port 8100. If you are using this default then you can connect to the running application by navigating your browser to http://localhost:8100.
Note that below we discuss some techniques for debugging Shiny applications, including the ability to stop execution and inspect the current environment. In order to combine these techniques with running your applications in a separate terminal session you need to run R interactively (that is, first type “R” to start an R session then execute runApp
from within the session).
When you make changes to your underlying user-interface definition or server script you don’t need to stop and restart your application to see the changes. Simply save your changes and then reload the browser to see the updated application in action.
One qualification to this: when a browser reload occurs Shiny explicitly checks the timestamps of the ui.R and server.R files to see if they need to be re-sourced. If you have other scripts or data files that change Shiny isn’t aware of those, so a full stop and restart of the application is necessary to see those changes reflected.
There are several techniques available for debugging Shiny applications. The first is to add calls to the cat function which print diagnostics where appropriate. For example, these two calls to cat print diagnostics to standard output and standard error respectively:
cat("foo\n")
cat("bar\n", file=stderr())
The second technique is to add explicit calls to the browser function to interrupt execution and inspect the environment where browser was called from. Note that using browser requires that you start the application from an interactive session (as opposed to using R -e as described above).
For example, to unconditionally stop execution at a certain point in the code:
# Always stop execution here
browser()
You can also use this technique to stop only on certain conditions. For example, to stop the MPG application only when the user selects “Transmission” as the variable:
# Stop execution when the user selects "am"
browser(expr = identical(input$variable, "am"))
You can also set the R "error" option to automatically enter the browser when an error occurs:
# Immediately enter the browser when an error occurs
options(error = browser)
Alternatively, you can specify the recover function as your error handler, which will print a list of the call stack and allow you to browse at any point in he stack:
# Call the recover function when an error occurs
options(error = recover)
If you want to set the error option automatically for every R session, you can do this in your .Rprofile file as described in this article on R Startup.
The Sliders application demonstrates the many capabilities of slider controls, including the ability to run an animation sequence. To run the example type:
> library(shiny)
> runExample("05_sliders")
Shiny slider controls are extremely capable and customizable. Features supported include:
Slider controls are created by calling the sliderInput
function. The ui.R file demonstrates using sliders with a variety of options:
library(shiny)
# Define UI for slider demo application
shinyUI(pageWithSidebar(
# Application title
headerPanel("Sliders"),
# Sidebar with sliders that demonstrate various available options
sidebarPanel(
# Simple integer interval
sliderInput("integer", "Integer:",
min=0, max=1000, value=500),
# Decimal interval with step value
sliderInput("decimal", "Decimal:",
min = 0, max = 1, value = 0.5, step= 0.1),
# Specification of range within an interval
sliderInput("range", "Range:",
min = 1, max = 1000, value = c(200,500)),
# Provide a custom currency format for value display, with basic animation
sliderInput("format", "Custom Format:",
min = 0, max = 10000, value = 0, step = 2500,
format="$#,##0", locale="us", animate=TRUE),
# Animation with custom interval (in ms) to control speed, plus looping
sliderInput("animation", "Looping Animation:", 1, 2000, 1, step = 10,
animate=animationOptions(interval=300, loop=T))
),
# Show a table summarizing the values entered
mainPanel(
tableOutput("values")
)
))
The server side of the Slider application is very straightforward: it creates a data frame containing all of the input values and then renders it as an HTML table:
library(shiny)
# Define server logic for slider examples
shinyServer(function(input, output) {
# Reactive expression to compose a data frame containing all of the values
sliderValues <- reactive({
# Compose data frame
data.frame(
Name = c("Integer",
"Decimal",
"Range",
"Custom Format",
"Animation"),
Value = as.character(c(input$integer,
input$decimal,
paste(input$range, collapse=' '),
input$format,
input$animation)),
stringsAsFactors=FALSE)
})
# Show the values using an HTML table
output$values <- renderTable({
sliderValues()
})
})
The Tabsets application demonstrates using tabs to organize output. To run the example type:
> library(shiny)
> runExample("06_tabsets")
Tabsets are created by calling the tabsetPanel
function with a list of tabs created by the tabPanel
function. Each tab panel is provided a list of output elements which are rendered vertically within the tab.
In this example we updated our Hello Shiny application to add a summary and table view of the data, each rendered on their own tab. Here is the revised source code for the user-interface:
library(shiny)
# Define UI for random distribution application
shinyUI(pageWithSidebar(
# Application title
headerPanel("Tabsets"),
# Sidebar with controls to select the random distribution type
# and number of observations to generate. Note the use of the br()
# element to introduce extra vertical spacing
sidebarPanel(
radioButtons("dist", "Distribution type:",
list("Normal" = "norm",
"Uniform" = "unif",
"Log-normal" = "lnorm",
"Exponential" = "exp")),
br(),
sliderInput("n",
"Number of observations:",
value = 500,
min = 1,
max = 1000)
),
# Show a tabset that includes a plot, summary, and table view
# of the generated distribution
mainPanel(
tabsetPanel(
tabPanel("Plot", plotOutput("plot")),
tabPanel("Summary", verbatimTextOutput("summary")),
tabPanel("Table", tableOutput("table"))
)
)
))
Introducing tabs into our user-interface underlines the importance of creating reactive expressions for shared data. In this example each tab provides its own view of the dataset. If the dataset is expensive to compute then our user-interface might be quite slow to render. The server script below demonstrates how to calculate the data once in a reactive expression and have the result be shared by all of the output tabs:
library(shiny)
# Define server logic for random distribution application
shinyServer(function(input, output) {
# Reactive expression to generate the requested distribution. This is
# called whenever the inputs change. The renderers defined
# below then all use the value computed from this expression
data <- reactive({
dist <- switch(input$dist,
norm = rnorm,
unif = runif,
lnorm = rlnorm,
exp = rexp,
rnorm)
dist(input$n)
})
# Generate a plot of the data. Also uses the inputs to build the
# plot label. Note that the dependencies on both the inputs and
# the 'data' reactive expression are both tracked, and all expressions
# are called in the sequence implied by the dependency graph
output$plot <- renderPlot({
dist <- input$dist
n <- input$n
hist(data(),
main=paste('r', dist, '(', n, ')', sep=''))
})
# Generate a summary of the data
output$summary <- renderPrint({
summary(data())
})
# Generate an HTML table view of the data
output$table <- renderTable({
data.frame(x=data())
})
})
The More Widgets application demonstrates the help text and submit button widgets as well as the use of embedded HTML elements to customize formatting. To run the example type:
> library(shiny)
> runExample("07_widgets")
In this example we update the Shiny Text application with some additional controls and formatting, specifically:
helpText
control to provide additional clarifying text alongside our input controls.submitButton
control to indicate that we don’t want a live connection between inputs and outputs, but rather to wait until the user clicks that button to update the output. This is especially useful if computing output is computationally expensive.h4
elements (heading level 4) into the output pane. Shiny offers a variety of functions for including HTML elements directly in pages including headings, paragraphics, links, and more.Here is the updated source code for the user-interface:
library(shiny)
# Define UI for dataset viewer application
shinyUI(pageWithSidebar(
# Application title.
headerPanel("More Widgets"),
# Sidebar with controls to select a dataset and specify the number
# of observations to view. The helpText function is also used to
# include clarifying text. Most notably, the inclusion of a
# submitButton defers the rendering of output until the user
# explicitly clicks the button (rather than doing it immediately
# when inputs change). This is useful if the computations required
# to render output are inordinately time-consuming.
sidebarPanel(
selectInput("dataset", "Choose a dataset:",
choices = c("rock", "pressure", "cars")),
numericInput("obs", "Number of observations to view:", 10),
helpText("Note: while the data view will show only the specified",
"number of observations, the summary will still be based",
"on the full dataset."),
submitButton("Update View")
),
# Show a summary of the dataset and an HTML table with the requested
# number of observations. Note the use of the h4 function to provide
# an additional header above each output section.
mainPanel(
h4("Summary"),
verbatimTextOutput("summary"),
h4("Observations"),
tableOutput("view")
)
))
All of the changes from the original Shiny Text application were to the user-interface, the server script remains the same:
library(shiny)
library(datasets)
# Define server logic required to summarize and view the selected dataset
shinyServer(function(input, output) {
# Return the requested dataset
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
# Generate a summary of the dataset
output$summary <- renderPrint({
dataset <- datasetInput()
summary(dataset)
})
# Show the first "n" observations
output$view <- renderTable({
head(datasetInput(), n = input$obs)
})
})
Sometimes you’ll want users to be able to upload their own data to your application. Shiny makes it easy to offer your users file uploads straight from the browser, which you can then access from your server logic.
Important notes:
shiny.maxRequestSize
option. For example, adding options(shiny.maxRequestSize=30*1024^2)
to the top of server.R
would increase the limit to 30MB.To run this example, type:
> library(shiny)
> runExample("09_upload")
File upload controls are created by using the fileInput
function in your ui.R
file. You access the uploaded data similarly to other types of input: by referring to input$inputId
. The fileInput
function takes a multiple
parameter that can be set to TRUE
to allow the user to select multiple files, and an accept
parameter can be used to give the user clues as to what kind of files the application expects.
shinyUI(pageWithSidebar(
headerPanel("CSV Viewer"),
sidebarPanel(
fileInput('file1', 'Choose CSV File',
accept=c('text/csv', 'text/comma-separated-values,text/plain')),
tags$hr(),
checkboxInput('header', 'Header', TRUE),
radioButtons('sep', 'Separator',
c(Comma=',',
Semicolon=';',
Tab='\t'),
'Comma'),
radioButtons('quote', 'Quote',
c(None='',
'Double Quote'='"',
'Single Quote'="'"),
'Double Quote')
),
mainPanel(
tableOutput('contents')
)
))
shinyServer(function(input, output) {
output$contents <- renderTable({
# input$file1 will be NULL initially. After the user selects and uploads a
# file, it will be a data frame with 'name', 'size', 'type', and 'datapath'
# columns. The 'datapath' column will contain the local filenames where the
# data can be found.
inFile <- input$file1
if (is.null(inFile))
return(NULL)
read.csv(inFile$datapath, header=input$header, sep=input$sep, quote=input$quote)
})
})
This example receives a file and attempts to read it as comma-separated values using read.csv
, then displays the results in a table. As the comment in server.R
indicates, inFile
is either NULL
or a dataframe that contains one row per uploaded file. In this case, fileInput
did not have the multiple
parameter so we can assume there is only one row.
The file contents can be accessed by reading the file named by the datapath
column. See the ?fileInput
help topic to learn more about the other columns that are available.
The examples so far have demonstrated outputs that appear directly in the page, such as plots, tables, and text boxes. Shiny also has the ability to offer file downloads that are calculated on the fly, which makes it easy to build data exporting features.
To run the example below, type:
> library(shiny)
> runExample("10_download")
You define a download using the downloadHandler
function on the server side, and either downloadButton
or downloadLink
in the UI:
shinyUI(pageWithSidebar(
headerPanel('Download Example'),
sidebarPanel(
selectInput("dataset", "Choose a dataset:",
choices = c("rock", "pressure", "cars")),
downloadButton('downloadData', 'Download')
),
mainPanel(
tableOutput('table')
)
))
shinyServer(function(input, output) {
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
output$table <- renderTable({
datasetInput()
})
output$downloadData <- downloadHandler(
filename = function() { paste(input$dataset, '.csv', sep='') },
content = function(file) {
write.csv(datasetInput(), file)
}
)
})
As you can see, downloadHandler
takes a filename
argument, which tells the web browser what filename to default to when saving. This argument can either be a simple string, or it can be a function that returns a string (as is the case here).
The content
argument must be a function that takes a single argument, the file name of a non-existent temp file. The content
function is responsible for writing the contents of the file download into that temp file.
Both the filename
and content
arguments can use reactive values and expressions (although in the case of filename
, be sure your argument is an actual function; filename = paste(input$dataset, '.csv')
is not going to work the way you want it to, since it is evaluated only once, when the download handler is being defined).
Generally, those are the only two arguments you’ll need. There is an optional contentType
argument; if it is NA
or NULL
, Shiny will attempt to guess the appropriate value based on the filename. Provide your own content type string (e.g. "text/plain"
) if you want to override this behavior.
Shiny apps are often more than just a fixed set of controls that affect a fixed set of outputs. Inputs may need to be shown or hidden depending on the state of another input, or input controls may need to be created on-the-fly in response to user input.
Shiny currently has three different approaches you can use to make your interfaces more dynamic. From easiest to most difficult, they are:
conditionalPanel
function, which is used in ui.R
and wraps a set of UI elements that need to be dynamically shown/hiddenrenderUI
function, which is used in server.R
in conjunction with the htmlOutput
function in ui.R
, lets you generate calls to UI functions and make the results appear in a predetermined place in the UILet’s take a closer look at each approach.
conditionalPanel
creates a panel that shows and hides its contents depending on the value of a JavaScript expression. Even if you don’t know any JavaScript, simple comparison or equality operations are extremely easy to do, as they look a lot like R (and many other programming languages).
Here’s an example for adding an optional smoother to a ggplot, and choosing its smoothing method:
# Partial example
checkboxInput("smooth", "Smooth"),
conditionalPanel(
condition = "input.smooth == true",
selectInput("smoothMethod", "Method",
list("lm", "glm", "gam", "loess", "rlm"))
)
In this example, the select control for smoothMethod
will appear only when the smooth
checkbox is checked. Its condition is "input.smooth == true"
, which is a JavaScript expression that will be evaluated whenever any inputs/outputs change.
The condition can also use output
values; they work in the same way (output.foo
gives you the value of the output foo
). If you have a situation where you wish you could use an R expression as your condition
argument, you can create a reactive expression in server.R
and assign it to a new output, then refer to that output in your condition
expression. For example:
# Partial example
selectInput("dataset", "Dataset", c("diamonds", "rock", "pressure", "cars")),
conditionalPanel(
condition = "output.nrows",
checkboxInput("headonly", "Only use first 1000 rows"))
# Partial example
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
output$nrows <- reactive({
nrow(datasetInput())
})
However, since this technique requires server-side calculation (which could take a long time, depending on what other reactive expressions are executing) we recommend that you avoid using output
in your conditions unless absolutely necessary.
Note: This feature should be considered experimental. Let us know whether you find it useful.
Sometimes it’s just not enough to show and hide a fixed set of controls. Imagine prompting the user for a latitude/longitude, then allowing the user to select from a checklist of cities within a certain radius. In this case, you can use the renderUI
expression to dynamically create controls based on the user’s input.
# Partial example
numericInput("lat", "Latitude"),
numericInput("long", "Longitude"),
uiOutput("cityControls")
# Partial example
output$cityControls <- renderUI({
cities <- getNearestCities(input$lat, input$long)
checkboxGroupInput("cities", "Choose Cities", cities)
})
renderUI
works just like renderPlot
, renderText
, and the other output rendering functions you’ve seen before, but it expects the expression it wraps to return an HTML tag (or a list of HTML tags, using tagList
). These tags can include inputs and outputs.
In ui.R
, use a uiOutput
to tell Shiny where these controls should be rendered.
Note: This feature should be considered experimental. Let us know whether you find it useful.
You can use JavaScript/jQuery to modify the page directly. General instructions for doing so are outside the scope of this tutorial, except to mention an important additional requirement. Each time you add new inputs/outputs to the DOM, or remove existing inputs/outputs from the DOM, you need to tell Shiny. Our current recommendation is:
Shiny.unbindAll()
.Shiny.bindAll()
.If you are adding or removing many inputs/outputs at once, it’s fine to call Shiny.unbindAll()
once at the beginning and Shiny.bindAll()
at the end – it’s not necessary to put these calls around each individual addition or removal of inputs/outputs.
The HTML UI application demonstrates defining a Shiny user-interface using a standard HTML page rather than a ui.R script. To run the example type:
> library(shiny)
> runExample("08_html")
The previous examples in this tutorial used a ui.R file to build their user-interfaces. While this is a fast and convenient way to build user-interfaces, some appliations will inevitably require more flexiblity. For this type of application, you can define your user-interface directly in HTML. In this case there is no ui.R file and the directory structure looks like this:
<application-dir>
|-- www
|-- index.html
|-- server.R
In this example we re-write the front-end of the Tabsets application using HTML directly. Here is the source code for the new user-interface definition:
<html>
<head>
<script src="shared/jquery.js" type="text/javascript"></script>
<script src="shared/shiny.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="shared/shiny.css"/>
</head>
<body>
<h1>HTML UI</h1>
<p>
<label>Distribution type:</label><br />
<select name="dist">
<option value="norm">Normal</option>
<option value="unif">Uniform</option>
<option value="lnorm">Log-normal</option>
<option value="exp">Exponential</option>
</select>
</p>
<p>
<label>Number of observations:</label><br />
<input type="number" name="n" value="500" min="1" max="1000" />
</p>
<pre id="summary" class="shiny-text-output"></pre>
<div id="plot" class="shiny-plot-output"
style="width: 100%; height: 400px"></div>
<div id="table" class="shiny-html-output"></div>
</body>
</html>
There are few things to point out regarding how Shiny binds HTML elements back to inputs and outputs:
name
attribute.id
attribute to an output slot and by specifying the requisite css class for the element (in this case either shiny-text-output, shiny-plot-output, or shiny-html-output).With this technique you can create highly customized user-interfaces using whatever HTML, CSS, and JavaScript you like.
All of the changes from the original Tabsets application were to the user-interface, the server script remains the same:
library(shiny)
# Define server logic for random distribution application
shinyServer(function(input, output) {
# Reactive expression to generate the requested distribution. This is
# called whenever the inputs change. The output renderers defined
# below then all used the value computed from this expression
data <- reactive({
dist <- switch(input$dist,
norm = rnorm,
unif = runif,
lnorm = rlnorm,
exp = rexp,
rnorm)
dist(input$n)
})
# Generate a plot of the data. Also uses the inputs to build the
# plot label. Note that the dependencies on both the inputs and
# the data reactive expression are both tracked, and all expressions
# are called in the sequence implied by the dependency graph
output$plot <- renderPlot({
dist <- input$dist
n <- input$n
hist(data(),
main=paste('r', dist, '(', n, ')', sep=''))
})
# Generate a summary of the data
output$summary <- renderPrint({
summary(data())
})
# Generate an HTML table view of the data
output$table <- renderTable({
data.frame(x=data())
})
})
Where you define objects will determine where the objects are visible. There are three different levels of visibility that you’ll want to be aware of when writing Shiny apps. Some objects are visible within the server.R
code of each user session; other objects are visible in the server.R
code across all sessions (multiple users could use a shared variable); and yet others are visible in the server.R
and the ui.R
code across all user sessions.
In server.R
, when you call shinyServer()
, you pass it a function func
which takes two arguments, input
and output
:
shinyServer(func = function(input, output) {
# Server code here
# ...
})
The function that you pass to shinyServer()
is called once for each session. In other words, func
is called each time a web browser is pointed to the Shiny application.
Everything within this function is instantiated separately for each session. This includes the input
and output
objects that are passed to it: each session has its own input
and output
objects, visible within this function.
Other objects inside the function, such as variables and functions, are also instantiated for each session. In this example, each session will have its own variable named startTime
, which records the start time for the session:
shinyServer(function(input, output) {
startTime <- Sys.time()
# ...
})
You might want some objects to be visible across all sessions. For example, if you have large data structures, or if you have utility functions that are not reactive (ones that don’t involve the input
or output
objects), then you can create these objects once and share them across all user sessions, by placing them in server.R
, but outside of the call to shinyServer()
.
For example:
# A read-only data set that will load once, when Shiny starts, and will be
# available to each user session
bigDataSet <- read.csv('bigdata.csv')
# A non-reactive function that will be available to each user session
utilityFunction <- function(x) {
# Function code here
# ...
}
shinyServer(function(input, output) {
# Server code here
# ...
})
You could put bigDataSet
and utilityFunction
inside of the function passed to shinyServer()
, but doing so will be less efficient, because they will be created each time a user connects.
If the objects change, then the changed objects will be visible in every user session. But note that you would need to use the <<-
assignment operator to change bigDataSet
, because the <-
operator only assigns values in the local environment.
varA <- 1
varB <- 1
listA <- list(X=1, Y=2)
listB <- list(X=1, Y=2)
shinyServer(function(input, output) {
# Create a local variable varA, which will be a copy of the shared variable
# varA plus 1. This local copy of varA is not be visible in other sessions.
varA <- varA + 1
# Modify the shared variable varB. It will be visible in other sessions.
varB <<- varB + 1
# Makes a local copy of listA
listA$X <- 5
# Modify the shared copy of listB
listB$X <<- 5
# ...
})
Things work this way because server.R
is sourced when you start your Shiny app. Everything in the script is run immediately, including the call to shinyServer()
—but the function which is passed to shinyServer()
is called only when a web browser connects and a new session is started.
Objects defined in global.R
are similar to those defined in server.R
outside shinyServer()
, with one important difference: they are also visible to the code in ui.R
. This is because they are loaded into the global environment of the R session; all R code in a Shiny app is run in the global environment or a child of it.
In practice, there aren’t many times where it’s necessary to share variables between server.R
and ui.R
. The code in ui.R
is run once, when the Shiny app is started and it generates an HTML file which is cached and sent to each web browser that connects. This may be useful for setting some shared configuration options.
If you want to split the server or ui code into multiple files, you can use source(local=TRUE)
to load each file. You can think of this as putting the code in-line, so the code from the sourced files will receive the same scope as if you copied and pasted the text right there.
This example server.R
file shows how sourced files will be scoped:
# Objects in this file are shared across all sessions
source('all_sessions.R', local=TRUE)
shinyServer(function(input, output) {
# Objects in this file are defined in each session
source('each_session.R', local=TRUE)
output$text <- renderText({
# Objects in this file are defined each time this function is called
source('each_call.R', local=TRUE)
# ...
})
})
If you use the default value of local=FALSE
, then the file will be sourced in the global environment.
On the server side, Shiny applications use the input
object to receive user input from the client web browser. The values in input
are set by UI objects on the client web page. There are also non-input values (in the sense that the user doesn’t enter these values through UI components) that are stored in an object called session$clientData
. These values include the URL, the pixel ratio (for high-resolution “Retina” displays), the hidden state of output objects, and the height and width of plot outputs.
To access session$clientData
values, you need to pass a function to shinyServer()
that takes session
as an argument (session
is a special object that is used for finer control over a user’s app session). Once it’s in there, you can access session$clientData
just as you would input
.
In the example below, the client browser will display out the components of the URL and also parse and print the query/search string (the part of the URL after a ”?
”):
shinyServer(function(input, output, session) {
# Return the components of the URL in a string:
output$urlText <- renderText({
paste(sep = "",
"protocol: ", session$clientData$url_protocol, "\n",
"hostname: ", session$clientData$url_hostname, "\n",
"pathname: ", session$clientData$url_pathname, "\n",
"port: ", session$clientData$url_port, "\n",
"search: ", session$clientData$url_search, "\n"
)
})
# Parse the GET query string
output$queryText <- renderText({
query <- parseQueryString(session$clientData$url_search)
# Return a string with key-value pairs
paste(names(query), query, sep = "=", collapse=", ")
})
})
shinyUI(bootstrapPage(
h3("URL components"),
verbatimTextOutput("urlText"),
h3("Parsed query string"),
verbatimTextOutput("queryText")
))
This app will display the following:
The values in session$clientData
will depend to some extent on the outputs. For example, a plot output object will report its height, width, and hidden status. The app below has a plot output, and displays all the values in session$clientData
:
shinyServer(function(input, output, session) {
# Store in a convenience variable
cdata <- session$clientData
# Values from cdata returned as text
output$clientdataText <- renderText({
cnames <- names(cdata)
allvalues <- lapply(cnames, function(name) {
paste(name, cdata[[name]], sep=" = ")
})
paste(allvalues, collapse = "\n")
})
# A histogram
output$myplot <- renderPlot({
hist(rnorm(input$obs), main="Generated in renderPlot()")
})
})
Notice that, just as with input
, values in session$clientData
can be accessed with session$clientData$myvar
or session$clientData[['myvar']]
. Or, equivalently, since we’ve saved it into a convenience variable cdata
, we can use cdata$myvar
or cdata[['myvar']]
.
shinyUI(pageWithSidebar(
headerPanel("Shiny Client Data"),
sidebarPanel(
sliderInput("obs", "Number of observations:",
min = 0, max = 1000, value = 500)
),
mainPanel(
h3("clientData values"),
verbatimTextOutput("clientdataText"),
plotOutput("myplot")
)
))
For the plot output output$myplot
, there are three entries in clientData
:
output_myplot_height
: The height of the plot on the web page, in pixels.output_myplot_width
: The width of the plot on the web page, in pixels.output_myplot_hidden
: If the object is hidden (not visible), this is TRUE. This is used because Shiny will by default suspend the output object when it is hidden. When suspended, the observer will not execute even when its inputs change.Here is the view from the client, with all the clientData
values:
When you want to have R generate a plot and send it to the client browser, the renderPlot()
function will in most cases do the job. But when you need finer control over the process, you might need to use the renderImage()
function instead.
renderPlot()
is useful for any time where R generates an image using its normal graphical device system. In other words, any plot-generating code that would normally go between png()
and dev.off()
can be used in renderPlot()
. If the following code works from the console, then it should work in renderPlot()
:
png()
# Your plotting code here
dev.off()
# This would go in shinyServer()
output$myPlot <- renderPlot({
# Your plotting code here
})
renderPlot()
takes care of a number of details automatically: it will resize the image to fit the output window, and it will even increase the resolution of the output image when displaying on high-resolution (“Retina”) screens.
The limitation to renderPlot()
is that it won’t send just any image file to the browser – the image must be generated by code that uses R’s graphical output device system. Other methods of creating images can’t be sent by renderPlot()
. For example, the following won’t work:
writePNG()
function from the png package.rgl.snapshot()
function, which creates images from 3D plots made with the rgl package.The solution in these cases is the renderImage()
function.
Image files can be sent using renderImage()
. The expression that you pass to renderImage()
must return a list containing an element named src
, which is the path to the file. Here is a very basic example of a Shiny app with an output that generates a plot and sends it with renderImage()
:
shinyServer(function(input, output, session) {
output$myImage <- renderImage({
# A temp file to save the output.
# This file will be removed later by renderImage
outfile <- tempfile(fileext='.png')
# Generate the PNG
png(outfile, width=400, height=300)
hist(rnorm(input$obs), main="Generated in renderImage()")
dev.off()
# Return a list containing the filename
list(src = outfile,
contentType = 'image/png',
width = 400,
height = 300,
alt = "This is alternate text")
}, deleteFile = TRUE)
})
shinyUI(pageWithSidebar(
headerPanel("renderImage example"),
sidebarPanel(
sliderInput("obs", "Number of observations:",
min = 0, max = 1000, value = 500)
),
mainPanel(
# Use imageOutput to place the image on the page
imageOutput("myImage")
)
))
Each time this output object is re-executed, it creates a new PNG file, saves a plot to it, then returns a list containing the filename along with some other values.
Because the deleteFile
argument is TRUE
, Shiny will delete the file (specified by the src
element) after it sends the data. This is appropriate for a case like this, where the image is created on-the-fly, but it wouldn’t be appropriate when, for example, your app sends pre-rendered images.
In this particular case, the image file is created with the png()
function. But it just as well could have been created with writePNG()
from the png package, or by any other method. If you have the filename of the image, you can send it with renderImage()
.
The list returned in the example above contains the following:
src
: The output file path.contentType
: The MIME type of the file. If this is missing, Shiny will try to autodetect the MIME type, from the file extension.width
and height
: The desired output size, in pixels.alt
: Alternate text for the image.Except for src
and contentType
, all values are passed through directly to the <img>
DOM element on the web page. The effect is similar to having an image tag with the following:
<img src="..." width="400" height="300" alt="This is alternate text">
Note that the src="..."
is shorthand for a longer URL. For browsers that support the data URI scheme, the src
and contentType
from the returned list are put together to create a special URL that embeds the data, so the result would be similar to something like this:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAm0AAAGnCAYAAADlkGDxAAAACXBIWXMAAAsTAAALEwEAmpwYAAAgAElEQVR4nOydd3ic1ZX/P2+ZKmlU"
width="400" height="300" alt="This is alternate text">
For browsers that don’t support the data URI scheme, Shiny sends a URL that points to the file.
If your Shiny app has pre-rendered images saved in a subdirectory, you can send them using renderImage()
. Suppose the images are in the subdirectory images/
, and are named image1.jpeg
, image2.jpeg
, and so on. The following code would send the appropriate image, depending on the value of input$n
:
shinyServer(function(input, output, session) {
# Send a pre-rendered image, and don't delete the image after sending it
output$preImage <- renderImage({
# When input$n is 3, filename is ./images/image3.jpeg
filename <- normalizePath(file.path('./images',
paste('image', input$n, '.jpeg', sep='')))
# Return a list containing the filename and alt text
list(src = filename,
alt = paste("Image number", input$n))
}, deleteFile = FALSE)
})
In this example, deleteFile
is FALSE
because the images aren’t ephemeral; we don’t want Shiny to delete an image after sending it.
Note that this might be less efficient than putting images in www/images
and emitting HTML that points to the images, because in the latter case the image will be cached by the browser.
In the first example above, the plot size was fixed at 400 by 300 pixels. For dynamic resizing, it’s possible to use values from session$clientData
to detect the output size.
In the example below, the output object is output$myImage
, and the width and height on the client browser are sent via session$clientData$output_myImage_width
and session$clientData$output_myImage_height
. This example also uses session$clientData$pixelratio
to multiply the resolution of the image, so that it appears sharp on high-resolution (Retina) displays:
shinyServer(function(input, output, session) {
# A dynamically-sized plot
output$myImage <- renderImage({
# Read myImage's width and height. These are reactive values, so this
# expression will re-run whenever they change.
width <- session$clientData$output_myImage_width
height <- session$clientData$output_myImage_height
# For high-res displays, this will be greater than 1
pixelratio <- session$clientData$pixelratio
# A temp file to save the output.
outfile <- tempfile(fileext='.png')
# Generate the image file
png(outfile, width=width*pixelratio, height=height*pixelratio,
res=72*pixelratio)
hist(rnorm(input$obs))
dev.off()
# Return a list containing the filename
list(src = outfile,
width = width,
height = height,
alt = "This is alternate text")
}, deleteFile = TRUE)
# This code reimplements many of the features of `renderPlot()`.
# The effect of this code is very similar to:
# renderPlot({
# hist(rnorm(input$obs))
# })
})
The width
and height
values passed to png()
specify the pixel dimensions of the saved image. These can differ from the width
and height
values in the returned list: those values are the pixel dimensions to used display the image. For high-res displays (where pixelratio
is 2), a “virtual” pixel in the browser might correspond to 2 x 2 physical pixels, and a double-resolution image will make use of each of the physical pixels.
It’s easy to build interactive applications with Shiny, but to get the most out of it, you’ll need to understand the reactive programming model used by Shiny.
In Shiny, there are three kinds of objects in reactive programming: reactive sources, reactive conductors, and reactive endpoints, which are represented with these symbols:
The simplest structure of a reactive program involves just a source and an endpoint:
In a Shiny application, the source typically is user input through a browser interface. For example, when the selects an item, types input, or clicks on a button, these actions will set values that are reactive sources. A reactive endpoint is usually something that appears in the user’s browser window, such as a plot or a table of values.
In a simple Shiny application, reactive sources are accessible through the input
object, and reactive endpoints are accessible through the output
object. (Actually, there are other possible kinds of sources and endpoints, which we’ll talk about later, but for now we’ll just talk about input
and output
.)
This simple structure, with one source and one endpoint, is used by the 01_hello
example. The server.R
code for that example looks something like this:
shinyServer(function(input, output) {
output$distPlot <- renderPlot({
hist(rnorm(input$obs))
})
})
You can see it in action at http://glimmer.rstudio.com/shiny/01_hello/.
The output$distPlot
object is a reactive endpoint, and it uses the reactive source input$obs
. Whenever input$obs
changes, output$distPlot
is notified that it needs to re-execute. In traditional program with an interactive user interface, this might involve setting up event handlers and writing code to read values and transfer data. Shiny does all these things for you behind the scenes, so that you can simply write code that looks like regular R code.
A reactive source can be connected to multiple endpoints, and vice versa. Here is a slightly more complex Shiny application:
shinyServer(function(input, output) {
output$plotOut <- renderPlot({
hist(faithful$eruptions, breaks = as.numeric(input$nBreaks))
if (input$individualObs)
rug(faithful$eruptions)
})
output$tableOut <- renderTable({
if (input$individualObs)
faithful
else
NULL
})
})
In a Shiny application, there’s no need to explictly describe each of these relationships and tell R what to do when each input component changes; Shiny automatically handles these details for you.
In an app with the structure above, whenever the value of the input$nBreaks
changes, the expression that generates the plot will automatically re-execute. Whenever the value of the input$individualObs
changes, the plot and table functions will automatically re-execute. (In a Shiny application, most endpoint functions have their results automatically wrapped up and sent to the web browser.)
So far we’ve seen reactive sources and reactive endpoints, and most simple examples use just these two components, wiring up sources directly to endpoints. It’s also possible to put reactive components in between the sources and endpoints. These components are called reactive conductors.
A conductor can both be a dependent and have dependents. In other words, it can be both a parent and child in a graph of the reactive structure. Sources can only be parents (they can have dependents), and endpoints can only be children (they can be dependents) in the reactive graph.
Reactive conductors can be useful for encapsulating slow or computationally expensive operations. For example, imagine that you have this application that takes a value input$n
and prints the nth value in the Fibonacci sequence, as well as the inverse of nth value in the sequence plus one (note the code in these examples is condensed to illustrate reactive concepts, and doesn’t necessarily represent coding best practices):
# Calculate nth number in Fibonacci sequence
fib <- function(n) ifelse(n<3, 1, fib(n-1)+fib(n-2))
shinyServer(function(input, output) {
output$nthValue <- renderText({ fib(as.numeric(input$n)) })
output$nthValueInv <- renderText({ 1 / fib(as.numeric(input$n)) })
})
The graph structure of this app is:
The fib()
algorithm is very inefficient, so we don’t want to run it more times than is absolutely necessary. But in this app, we’re running it twice! On a reasonably fast modern machine, setting input$n
to 30 takes about 15 seconds to calculate the answer, largely because fib()
is run twice.
The amount of computation can be reduced by adding a reactive conductor in between the source and endpoints:
fib <- function(n) ifelse(n<3, 1, fib(n-1)+fib(n-2))
shinyServer(function(input, output) {
currentFib <- reactive({ fib(as.numeric(input$n)) })
output$nthValue <- renderText({ currentFib() })
output$nthValueInv <- renderText({ 1 / currentFib() })
})
Here is the new graph structure:
Keep in mind that if your application tries to access reactive values or expressions from outside a reactive context — that is, outside of a reactive expression or observer — then it will result in an error. You can think of there being a reactive “world” which can see and change the non-reactive world, but the non-reactive world can’t do the same to the reactive world. Code like this will not work, because the call to fib()
is not in the reactive world (it’s not in a reactive()
or renderXX()
call) but it tries to access something that is, the reactive value input$n
:
shinyServer(function(input, output) {
# Will give error
currentFib <- fib(as.numeric(input$n))
output$nthValue <- renderText({ currentFib })
})
On the other hand, if currentFib
is a function that accesses a reactive value, and that function is called within the reactive world, then it will work:
shinyServer(function(input, output) {
# OK, as long as this is called from the reactive world:
currentFib <- function() {
fib(as.numeric(input$n))
}
output$nthValue <- renderText({ currentFib })
})
In this section, we’ve learned about:
We’ve discussed reactive sources, conductors, and endpoints. These are general terms for parts that play a particular role in a reactive program. Presently, Shiny has one class of objects that act as reactive sources, one class of objects that act as reactive conductors, and one class of objects that act as reactive endpoints, but in principle there could be other classes that implement these roles.
All of the examples use these three implementations, as there are presently no other implementations of the source, conductor, and endpoint roles.
Reactive values contain values (not surprisingly), which can be read by other reactive objects. The input
object is a ReactiveValues
object, which looks something like a list, and it contains many individual reactive values. The values in input
are set by input from the web browser.
We’ve seen reactive expressions in action, with the Fibonacci example above. They cache their return values, to make the app run more efficiently. Note that, abstractly speaking, reactive conductors do not necessarily cache return values, but in this implementation, reactive expressions, they do.
A reactive expressions can be useful for caching the results of any procedure that happens in response to user input, including:
Observers are similar to reactive expressions, but with a few important differences. Like reactive expressions, they can access reactive values and reactive expressions. However, they do not return any values, and therefore do not cache their return values. Instead of returning values, they have side effects – typically, this involves sending data to the web browser.
The output
object looks something like a list, and it can contain many individual observers.
If you look at the code for renderText()
and friends, you’ll see that they each return a function which returns a value. They’re typically used like this:
output$number <- renderText({ as.numeric(input$n) + 1 })
This might lead you to think that the observers do return values. However, this isn’t the whole story. The function returned by renderText()
is actually not an observer/endpoint. When it is assigned to output$x
, the function returned by renderText()
gets automatically wrapped into another function, which is an observer. The wrapper function is used because it needs to do special things to send the data to the browser.
Reactive expressions and observers are similar in that they store expressions that can be executed, but they have some fundamental differences.
At the core of Shiny is its reactive engine: this is how Shiny knows when to re-execute each component of an application. We’ll trace into some examples to get a better understanding of how it works.
At an abstract level, we can describe the 01_hello
example as containing one source and one endpoint. When we talk about it more concretely, we can describe it as having one reactive value, input$obs
, and one reactive observer, output$distPlot
.
shinyServer(function(input, output) {
output$distPlot <- renderPlot({
hist(rnorm(input$obs))
})
})
As shown in the diagram below, a reactive value has a value. A reactive observer, on the other hand, doesn’t have a value. Instead, it contains an R expression which, when executed, has some side effect (in most cases, this involves sending data to the web browser). But the observer doesn’t return a value. Reactive observers have another property: they have a flag that indicates whether they have been invalidated. We’ll see what that means shortly.
After you load this application in a web page, it be in the state shown above, with input$obs
having the value 500 (this is set in the ui.r
file, which isn’t shown here). The arrow represents the direction that invalidations will flow. If you change the value to 1000, it triggers a series of events that result in a new image being sent to your browser.
When the value of input$obs
changes, two things happen:
In this case, the only descendant is output$distPlot
:
Once all the descendants are invalidated, a flush occurs. When this happens, all invalidated observers re-execute.
Remember that the code we assigned to output$distPlot
makes use of input$obs
:
output$distPlot <- renderPlot({
hist(rnorm(input$obs))
})
As output$distPlot
re-executes, it accesses the reactive value input$obs
. When it does this, it becomes a dependent of that value, represented by the arrow . When input$obs
changes, it invalidates all of its children; in this case, that’s justoutput$distPlot
.
As it finishes executing, output$distPlot
creates a PNG image file, which is sent to the browser, and finally it is marked as clean (not invalidated).
Now the cycle is complete, and the application is ready to accept input again.
When someone first starts a session with a Shiny application, all of the endpoints start out invalidated, triggering this series of events.
Here’s the code for our Fibonacci program:
fib <- function(n) ifelse(n<3, 1, fib(n-1)+fib(n-2))
shinyServer(function(input, output) {
currentFib <- reactive({ fib(as.numeric(input$n)) })
output$nthValue <- renderText({ currentFib() })
output$nthValueInv <- renderText({ 1 / currentFib() })
})
Here’s the structure. It’s shown in its state after the initial run, with the values and invalidation flags (the starting value for input$n
is set in ui.r
, which isn’t displayed).
Suppose the user sets input$n
to 30. This is a new value, so it immediately invalidates its children, currentFib
, which in turn invalidates its children, output$nthValue
and output$nthValueInv
. As the invalidations are made, the invalidation arrows are removed:
After the invalidations finish, the reactive environment is flushed, so the endpoints re-execute. If a flush occurs when multiple endpoints are invalidated, there isn’t a guaranteed order that the endpoints will execute, so nthValue
may run before nthValueInv
, or vice versa. The execution order of endpoints will not affect the results, as long as they don’t modify and read non-reactive variables (which aren’t part of the reactive graph).
Suppose in this case that nthValue()
executes first. The next several steps are straightforward:
As output$nthValueInv()
executes, it calls currentFib()
. If currentFib()
were an ordinary R expression, it would simply re-execute, taking another several seconds. But it’s not an ordinary expression; it’s a reactive expression, and it now happens to be marked clean. Because it is clean, Shiny knows that all of currentFib
’s reactive parents have not changed values since the previous run currentFib()
. This means that running the function again would simply return the same value as the previous run. (Shiny assumes that the non-reactive objects used by currentFib()
also have not changed. If, for example, it called Sys.time()
, then a second run of currentFib()
could return a different value. If you wanted the changing values of Sys.time()
to be able to invalidate currentFib()
, it would have to be wrapped up in an object that acted as a reactive source. If you were to do this, that object would also be added as a node on the reactive graph.)
Acting on this assumption. that clean reactive expressions will return the same value as they did the previous run, Shiny caches the return value when reactive expressions are executed. On subsequent calls to the reactive expression, it simply returns the cached value, without re-executing the expression, as long as it remains clean.
In our example, when output$nthValueInv()
calls currentFib()
, Shiny just hands it the cached value, 832040. This happens almost instantaneously, instead of taking several more seconds to re-execute currentFib()
:
Finally, output$nthValueInv()
takes that value, finds the inverse, and then as a side effect, sends the value to the browser.
In this section we’ve learned about:
Sometimes it’s useful for an observer/endpoint to access a reactive value or expression, but not to take a dependency on it. For example, if the observer performs a long calculation or downloads large data set, you might want it to execute only when a button is clicked.
For this, we’ll use actionButton
. We’ll define a ui.R
that is a slight modification of the one from 01_hello – the only difference is that it has an actionButton labeled “Go!”. You can see it in action at http://glimmer.rstudio.com/winston/actionbutton/.
The actionButton includes some JavaScript code that sends numbers to the server. When the web browser first connects, it sends a value of 0, and on each click, it sends an incremented value: 1, 2, 3, and so on.
shinyUI(pageWithSidebar(
headerPanel("Click the button"),
sidebarPanel(
sliderInput("obs", "Number of observations:",
min = 0, max = 1000, value = 500),
actionButton("goButton", "Go!")
),
mainPanel(
plotOutput("distPlot")
)
))
In our server.R
, there are two changes to note. First, output$distPlot
will take a dependency on input$goButton
, simply by accessing it. When the button is clicked, the value of input$goButton
increases, and so output$distPlot
re-executes.
The second change is that the access to input$obs
is wrapped with isolate()
. This function takes an R expression, and it tells Shiny that the calling observer or reactive expression should not take a dependency on any reactive objects inside the expression.
shinyServer(function(input, output) {
output$distPlot <- renderPlot({
# Take a dependency on input$goButton
input$goButton
# Use isolate() to avoid dependency on input$obs
dist <- isolate(rnorm(input$obs))
hist(dist)
})
})
The resulting graph looks like this:
And here’s a walkthrough of the process when input$obs
is set to 1000, and then the Go button is clicked:
In the actionButton
example, you might want to prevent it from returning a plot the first time, before the button has been clicked. Since the starting value of an actionButton
is zero, this can be accomplished with the following:
output$distPlot <- renderPlot({
if (input$goButton == 0)
return()
# plot-making code here
})
Reactive values are not the only things that can be isolated; reactive expressions can also be put inside an isolate()
. Building off the Fibonacci example from above, this would calculate the nth value only when the button is clicked:
output$nthValue <- renderText({
if (input$goButton == 0)
return()
isolate({ fib(as.numeric(input$n)) })
})
It’s also possible to put multiple lines of code in isolate()
. For example here are some blocks of code that have equivalent effect:
# Separate calls to isolate -------------------------------
x <- isolate({ input$xSlider }) + 100
y <- isolate({ input$ySlider }) * 2
z <- x/y
# Single call to isolate ----------------------------------
isolate({
x <- input$xSlider + 100
y <- input$ySlider * 2
z <- x/y
})
# Single call to isolate, use return value ----------------
z <- isolate({
x <- input$xSlider + 100
y <- input$ySlider * 2
x/y
})
In all of these cases, the calling function won’t take a reactive dependency on either of the input
variables.
Once you’ve written your Shiny app, you can make it available to anyone who has a web browser, using our Shiny Server software. You can either host the applications on your own server, or let us host your Shiny applications for you.
If you want a simple way to distribute your Shiny app so that users can run them on their own computers, see Deploying Shiny Apps to Run Locally.
With our Shiny Server software, you can deploy Shiny applications over the web so that users need only a web browser and your application’s URL. You’ll need a Linux server and Shiny Server.
Shiny Server is free and open source, though in the future we will offer a commercially licensed edition with additional features for larger organizations. If you’d like to be notified of future beta releases of Shiny Server, please register now.
Want to deploy over the web but prefer not to run your own server? We’re currently beta testing a subscription-based hosting service for Shiny. To apply for a free beta test account, register now.
Once you’ve written your Shiny app, you can distribute it for others to run on their own computers—they can download and run Shiny apps with a single R command. This requires that they have R and Shiny installed on their computers.
If you want your Shiny app to be accessible over the web, so that users only need a web browser, see Deploying Shiny Apps over the Web.
Here are some ways to deliver Shiny apps to run locally:
One easy way is to put your code on gist.github.com, a code pasteboard service from GitHub. Both server.R and ui.R must be included in the same gist, and you must use their proper filenames. See https://gist.github.com/3239667 for an example.
Your recipient must have R and the Shiny package installed, and then running the app is as easy as entering the following command:
shiny::runGist('3239667')
In place of '3239667'
you will use your gist’s ID; or, you can use the entire URL of the gist (e.g. 'https://gist.github.com/3239667'
).
If your project is stored in a git repository on GitHub, then others can download and run your app directly. An example repository is at https://github.com/rstudio/shiny_example. The following command will download and run the application:
shiny::runGitHub('shiny_example', 'rstudio')
In this example, the GitHub account is 'rstudio'
and the repository is 'shiny_example'
; you will need to replace them with your account and repository name.
If you store a zip or tar file of your project on a web or FTP server, users can download and run it with a command like this:
runUrl('https://github.com/rstudio/shiny_example/archive/master.zip')
The URL in this case is a zip file that happens to be stored on GitHub; replace it with the URL to your zip file.
Another way is to simply zip up your project directory and send it to your recipient(s), where they can unzip the file and run it the same way you do (shiny::runApp
).
If your Shiny app is useful to a broader audience, it might be worth the effort to turn it into an R package. Put your Shiny application directory under the package’s inst
directory, then create and export a function that contains something like this:
shiny::runApp(system.file('appdir', package='packagename'))
where appdir
is the name of your app’s subdirectory in inst
, and packagename
is the name of your package.
Shiny comes equipped with a variety of useful input components, but as you build more ambitious applications, you may find yourself needing input widgets that we don’t include. Fortunately, Shiny is designed to let you create your own custom input components. If you can implement it using HTML, CSS, and JavaScript, you can use it as a Shiny input!
(If you’re only familiar with R and not with HTML/CSS/JavaScript, then you will likely find it tough to create all but the simplest custom input components on your own. However, other people can – and hopefully will – bundle up their custom Shiny input components as R packages and make them available to the rest of the community.)
The first steps in creating a custom input component is no different than in any other form of web development. You write HTML markup that lays out the component, CSS rules to style it, and use JavaScript (mostly event handlers) to give it behavior, if necessary.
Shiny input components should try to adhere to the following principles, if possible:
When used in a Shiny application, your component’s HTML markup will be repeated once for each instance of the component on the page, but the CSS and JavaScript will generally only need to appear once, most likely in the <head>
. For R-based interface code, you can use the functions singleton
and tags$head
together to ensure these tags appear once and only once, in the head. (See the full example below.)
Each custom input component also needs an input binding, an object you create that tells Shiny how to identify instances of your component and how to interact with them. (Note that each instance of the input component doesn’t need its own input binding object; rather, all instances of a particular type of input component share a single input binding object.)
An input binding object needs to have the following methods:
find(scope)
Given an HTML document or element (scope
), find any descendant elements that are an instance of your component and return them as an array (or array-like object). The other input binding methods all take an el
argument; that value will always be an element that was returned from find
.
A very common implementation is to use jQuery's find
method to identify elements with a specific class, for example:
exampleInputBinding.find = function(scope) { return $(scope).find(".exampleComponentClass"); };
getId(el)
el
, or null
if the element doesn't have an ID and should therefore be ignored. The default implementation in Shiny.InputBinding
reads the data-input-id
attribute and falls back to the element's id
if not present.
getValue(el)
el
. This can be any JSON-compatible value.
setValue(el, value)
subscribe(el, callback)
Subscribe to DOM events on the element el
that indicate the value has changed. When the DOM events fire, call callback
(a function) which will tell Shiny to retrieve the value.
We recommend using jQuery's event namespacing feature when subscribing, as unsubscribing becomes very easy (see unsubscribe
, below). In this example, exampleComponentName
is used as a namespace:
exampleInputBinding.subscribe = function(el, callback) { $(el).on("keyup.exampleComponentName", function(event) { callback(true); }); $(el).on("change.exampleComponentName", function(event) { callback(); }); };
Later on, we can unsubscribe ".exampleComponentName"
which will remove all of our handlers without touching anyone else's.
The callback
function optionally takes an argument: a boolean value that indicates whether the component's rate policy should apply (true
means the rate policy should apply). See getRatePolicy
below for more details.
unsubscribe(el)
Unsubscribe DOM event listeners that were bound in subscribe
.
Example:
exampleInputBinding.unsubscribe = function(el) {
$(el).off(".exampleComponentName");
};
getRatePolicy()
Return an object that describes the rate policy of this component (or null
for default).
Rate policies are helpful for slowing down the rate at which input events get sent to the server. For example, as the user drags a slider from value A to value B, dozens of change events may occur. It would be wasteful to send all of those events to the server, where each event would potentially cause expensive computations to occur.
A rate policy slows down the rate of events using one of two algorithms (so far). Throttling means no more than one event will be sent per X milliseconds. Debouncing means all of the events will be ignored until no events have been received for X milliseconds, at which time the most recent event will be sent. This blog post goes into more detail about the difference between throttle and debounce.
A rate policy object has two members:
policy
- Valid values are the strings "direct"
, "debounce"
, and "throttle"
. "direct"
means that all events are sent immediately.
delay
- Number indicating the number of milliseconds that should be used when debouncing or throttling. Has no effect if the policy is direct
.
Rate policies are only applied when the callback
function in subscribe
is called with true
as the first parameter. It's important that input components be able to control which events are rate-limited and which are not, as different events may have different expectations to the user. For example, for a textbox, it would make sense to rate-limit events while the user is typing, but if the user hits Enter or focus leaves the textbox, then the input should always be sent immediately.
Once you’ve created an input binding object, you need to tell Shiny to use it:
Shiny.inputBindings.register(exampleInputBinding, "yourname.exampleInputBinding");
The second argument is a name the user can use to change the priority of the binding. On the off chance that the user has multiple bindings that all want to claim the same HTML element as their own, this call can be used to control the priority of the bindings:
Shiny.inputBindings.setPriority("yourname.exampleInputBinding", 10);
Higher numbers indicate a higher priority; the default priority is 0. All of Shiny’s built-in input component bindings default to a priority of 0.
If two bindings have the same priority value, then the more recently registered binding has the higher priority.
For this example, we’ll create a button that displays a number, whose value increases by one each time the button is clicked. Here’s what the end result will look like:
To start, let’s design the HTML markup for this component:
<button id="inputId" class="increment btn" type="button">0</button>
The CSS class increment
is what will differentiate our buttons from any other kind of buttons. (The btn
class is just to make the button look decent in Twitter Bootstrap.)
Now we’ll write the JavaScript that drives the button’s basic behavior:
$(document).on("click", "button.increment", function(evt) {
// evt.target is the button that was clicked
var el = $(evt.target);
// Set the button's text to its current value plus 1
el.text(parseInt(el.text()) + 1);
// Raise an event to signal that the value changed
el.trigger("change");
});
This code uses jQuery’s delegated events feature to bind all increment buttons at once.
Now we’ll create the Shiny binding object for our component, and register it:
var incrementBinding = new Shiny.InputBinding();
$.extend(incrementBinding, {
find: function(scope) {
return $(scope).find(".increment");
},
getValue: function(el) {
return parseInt($(el).text());
},
setValue: function(el, value) {
$(el).text(value);
},
subscribe: function(el, callback) {
$(el).on("change.incrementBinding", function(e) {
callback();
});
},
unsubscribe: function(el) {
$(el).off(".incrementBinding");
}
});
Shiny.inputBindings.register(incrementBinding);
Both the behavioral JavaScript code and the Shiny binding code should generally be run when the page loads. (It’s important that they run before Shiny initialization, which occurs after all the document ready event handlers are executed.)
The cleanest way to do this is to put both chunks of JavaScript into a file. In this case, we’ll use the path ./www/js/increment.js
, which we can then access as http://localhost:8100/js/increment.js
.
If you’re using an index.html
style user interface, you’ll just need to add this line to your <head>
(make sure it comes after the script tag that loads shiny.js
):
<script src="js/increment.js"></script>
On the other hand, if you’re using ui.R
, then you can define this function before the call to shinyUI
:
incrementButton <- function(inputId, value = 0) {
tagList(
singleton(tags$head(tags$script(src = "js/increment.js"))),
tags$button(id = inputId,
class = "increment btn",
type = "button",
as.character(value))
)
}
Then in your shinyUI
page definition you can call incrementButton
wherever you want an increment button rendered. Notice the line that begins with singleton
will ensure that the increment.js
file will be included just one time, in the <head>
, no matter how many buttons you insert into the page or where you place them.
Right out of the box, Shiny makes it easy to include plots, simple tables, and text as outputs in your application; but we imagine that you’ll also want to display outputs that don’t fit into those categories. Perhaps you need an interactive choropleth map or a googleVis motion chart.
Similar to custom inputs, if you have some knowledge of HTML/CSS/JavaScript you can also build reusable, custom output components. And you can bundle up output components as R packages for other Shiny users to use.
Start by deciding the kind of values your output component is going to receive from the user’s server side R code.
Whatever value the user’s R code returns is going to need to somehow be turned into a JSON-compatible value (Shiny uses RJSONIO to do the conversion). If the user’s code is naturally going to return something RJSONIO-compatible – like a character vector, a data frame, or even a list that contains atomic vectors – then you can just direct the user to use a function on the server. However, if the output needs to undergo some other kind of transformation, then you’ll need to write a wrapper function that your users will use instead (analogous to renderPlot
or renderTable
).
For example, if the user wants to output time series objects then you might create a renderTimeSeries
function that knows how to translate ts
objects to a simple list or data frame:
renderTimeSeries <- function(expr, env=parent.frame(), quoted=FALSE) {
# Convert the expression + environment into a function
func <- exprToFunction(expr, env, quoted)
function() {
val <- func()
list(start = tsp(val)[1],
end = tsp(val)[2],
freq = tsp(val)[3],
data = as.vector(val))
}
}
which would then be used by the user like so:
output$timeSeries1 <- renderTimeSeries({
ts(matrix(rnorm(300), 100, 3), start=c(1961, 1), frequency=12)
})
At this point, we’re ready to design the HTML markup and write the JavaScript code for our output component.
For many components, you’ll be able to have extremely simple HTML markup, something like this:
<div id="timeSeries1" class="timeseries-output"></div>
We’ll use the timeseries-output
CSS class as an indicator that the element is one that we should bind to. When new output values for timeSeries1
come down from the server, we’ll fill up the div with our visualization using JavaScript.
Each custom output component needs an output binding, an object you create that tells Shiny how to identify instances of your component and how to interact with them. (Note that each instance of the output component doesn’t need its own output binding object; rather, all instances of a particular type of output component share a single output binding object.)
An output binding object needs to have the following methods:
find(scope)
Given an HTML document or element (scope
), find any descendant elements that are an instance of your component and return them as an array (or array-like object). The other output binding methods all take an el
argument; that value will always be an element that was returned from find
.
A very common implementation is to use jQuery's find
method to identify elements with a specific class, for example:
exampleOutputBinding.find = function(scope) { return $(scope).find(".exampleComponentClass"); };
getId(el)
el
, or null
if the element doesn't have an ID and should therefore be ignored. The default implementation in Shiny.OutputBinding
reads the data-output-id
attribute and falls back to the element's id
if not present.
renderValue(el, data)
renderError(el, err)
err
is an object with a message
String property.
clearError(el)
el
is currently displaying an error, clear it.
Once you’ve created an output binding object, you need to tell Shiny to use it:
Shiny.outputBindings.register(exampleOutputBinding, "yourname.exampleOutputBinding");
The second argument is a string that uniquely identifies your output binding. At the moment it is unused but future features may depend on it.