Shiny简介

Shiny是RStudio公司开发的新包,有了它,可以用R语言轻松开发交互式web应用。

想查看更详细的介绍和实例,请访问Shiny的官方主页

特性

  • 只用几行代码就可以构建有用的web应用程序—不需要用JavaScript。
  • Shiny应用程序会自动刷新计算结果,这与电子表格实时计算的效果类似。 当用户修改输入时,输出值自动更新,而不需要在浏览器中手动刷新。
  • Shiny用户界面可以用纯R语言构建,如果想更灵活,可以直接用HTML、CSS和JavaScript来写。
  • 可以在任何R环境中运行(R命令行、Windows或Mac中的Rgui、ESS、StatET、RStudio等)
  • 基于Twitter Bootstrap的默认UI主题很吸引人。
  • 高度定制化的滑动条小工具(slider widget),内置了对动画的支持。
  • 预先构建有输出小工具,用来展示图形、表格以及打印输出R对象。
  • 采用websockets包,做到浏览器和R之间快速双向通信。
  • 采用反应式(reactive)编程模型,摒弃了繁杂的 事件处理代码,这样你可以集中精力于真正关心的代码上。
  • 开发和发布你自己的Shiny小工具,其他开发者也可以非常容易地将它加到自己的应用中(即将面市!)

安装

Shiny可以从CRAN获取, 所以你可以用通常的方式来安装,在R的命令行里输入:

r install.packages("shiny")

Let’s Go!

本教程涵盖了Shiny的基础知识,提供了详尽的案例来展示它的各种特性。点击Next按钮对Shiny说hello吧!

Hello Shiny Screenshot

Hello Shiny是个简单的应用程序, 这个程序可以生成正态分布的随机数,随机数个数可以由用户定义,并且绘制这些随机数的直方图. 要运行这个例子,只需键入:

library(shiny)
runExample("01_hello")

Shiny应用程序分为两个部分:用户界面定义和服务端脚本。这两部分的源代码将在下面列出。

在教程的后续章节,我们将解释代码的细节并讲解如何用“反应性”表达式来生成输出。现在,就尝试运行一下例子程序,浏览一下源代码,以获得对shiny的初始印象。也请认真阅读注释。

用户界面是在源文件ui.R中定义的:

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")
  )
))

下面列出了服务端的代码。从某种程度上说,它很简单——生成给定个数的随机变量, 然后将直方图画出来。不过,你也注意到了,返回图形的函数被 renderPlot包裹着。函数上面的注释对此做出了一些解释,不过如果你觉得还是搞不明白,不用担心——后面我们将更进一步解释这个概念。

server.R

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)
  })
})

下一个例子将展示其他输入控件的用法,以及生成文本输出的被动式函数的用法。

Tabsets Screenshot

Shiny Text这个应用程序展示的是直接打印R对象,以及用HTML表格展示数据框。要运行例子程序,只需键入:

> library(shiny)
> runExample("02_text")

前面那个例子里用一个滑动条来输入数值,并且输出图形。而这个例子更进了一步:有两个输入,以及两种类型的文本输出。

如果你改变观测个数, 将会发现Shiny应用程序的一大特性:输入和输出是结合在一起的,并且“实时”更新运算结果(就像Excel一样)。 在这个例子中,当观测个数发生变化时,只有表格更新,而不需要重新加载整个页面。

下面是用户界面定义的代码。请注意,sidebarPanelmainPanel的函数调用中有两个参数(对应于两个输入和两个输出)

ui.R

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")
  )
))

服务端的程序要稍微复杂一点。现在,我们创建:

  • 一个反应性表达式来返回用户选择的相应数据集。
  • 还有两个渲染表达式(rendering expressions,分别是renderPrintrenderTable),以返回 output$summaryoutput$view 的值。

这些表达式和第一个例子中的 renderPlot 运作方式类似:通过声明渲染表达式,你也就告诉了shiny,一旦渲染表达式所依赖的值(在这里例子中是两个用户输入值的任意一个:input$datasetinput$n)发生改变,表达式就会执行。。

server.R

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)
  })
})

我们已经介绍了一些反应性表达式的用法,但是并没有真正解释它们是如何运作的。下一个例子会以此为基础进一步讲解Shiny中反应性表达式的用法。

Reactivity Screenshot

Reactivity的示例程序与Hello Text很相似,但是用到了反应式编程里更多细节的概念,要运行该例子,请键入:

> library(shiny)
> runExample("03_reactivity")

前面几个例子给你了个初步印象——Shiny应用程序的代码长成什么样子。前面解释了反应式编程的一点概念,不过略过了大部分细节。 在本节,我们会更进一步讲解这些细节。如果你想更深入学习这些细节,请看《理解反应式设计》这节,从反应式设计概述开始。

什么是反应式设计

Shiny的web框架从本质上说是使从页面中获取输入值并传递给R变得更容易,然后把R代码的结果以输出值的形式返回给页面。

input values => R code => output values

因为Shiny程序是交互式的,输入值可以随时改变,输出值也应该立即更新,以反映输入输入值的改变。

Shiny中有反应式编程的库,你可以用它来定义你的应用程序逻辑。使用这个库,改变输入值会自动引发R代码中相应的部分重新执行,反过来会更新输出结果。

反应式编程基础

反应式编程是种编程风格,这种风格以反应值开始,反应值是随时间变化的值,或者由用户输入的值,在反应值之上绑定有反应表达式(reactive expression),反应表达式会接收到反应值并执行其他反应表达式。

反应表达式有趣的地方在于,当它执行的时候,会自动跟踪读取到的反应值以及调用的其他反应表达式。如果反应表达式所依赖的反应值和反应表达式发生了改变,那么该反应表达式的返回值也应该变化(原文是If those “dependencies” become out of date, then they know that their own return value has also become out of date)。因为有这种跟踪机制,所以改变一个反应值会自动引发依赖于它的反应表达式重新执行。

在shiny中使用反应值时,最常见的方式是使用input对象。input对象会被传递给shinyServer函数中,让你可以用类似列表的语法来访问网页上的输入值。从代码上看,你好像是从列表或者数据框里读取了值,但实际上你读取的是反应值。你不必写监测输入值变化的代码,只需要写反应表达式来读取所需的反应值,Shiny会处理好什么时候调用它们。

创建反应表达式很简单,只需要把一个正常的表达式传递给reactive函数就行。在本节的示例程序中,下面这个简单的反应表达式的功能是,基于用户在表单中选择的选项来返回R数据框。

datasetInput <- reactive({
   switch(input$dataset,
          "rock" = rock,
          "pressure" = pressure,
          "cars" = cars)
})

为了将反应值转化为可以在网页上呈现的输出,我们要将它们赋值给output对象(同样传递给shinyServer函数)。下面是个赋值给输出值的例子,输出值依赖于我们刚才定义的反应表达式datasetInput,以及input$obs

output$view <- renderTable({
   head(datasetInput(), n = input$obs)
})

不管是 datasetInput 还是input$obs,一旦它们的值发生改变,上面这个表达式将会重新执行(它的输出也会在浏览器里重新渲染)。

回到代码上

现在我们已经对一些核心概念有了更多了解,我们再来看看源代码,并尝试更深层次理解。用户接口的定义中,增加了来用定义说明文字(caption)的文本输入框,尽管它与前一个例子还是很相似。

ui.R

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")
  )
))

服务端脚本

服务端脚本声明了反应表达式datasetInput和三个反应输出值。下面有每个定义的详细注释描述了在反应式系统中是如何运作的:

server.R

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)
  })
})

在前三个例子中,我们查看了一些代码,也解释了不少概念。下一节将重点讲解从头开始构建shiny程序的机制,也会讲解如何运行和调试shiny程序。

UI & Server

我们来把构建简单的Shiny应用程序的流程走一遍。shiny程序是个简单的目录,里面包括用户接口的定义、服务端脚本以及起支持作用的数据、脚本和其他资源。

构建应用程序之初,先建一个空目录,在这个目录里创建空文件ui.Rserver.R。为了便于解释,我们假定你选择在~/shinyapp创建程序:

~/shinyapp
|-- ui.R
|-- server.R

现在我们将在每个源文件中添加所需的最少代码。先定义用户接口,调用函数pageWithSidebar并传递它的结果到shinyUI函数:

ui.R

library(shiny)

# Define UI for miles per gallon application
shinyUI(pageWithSidebar(

  # Application title
  headerPanel("Miles Per Gallon"),

  sidebarPanel(),

  mainPanel()
))

三个函数 headerPanelsidebarPanelmainPanel 定义了用户接口的不同区域。 程序将会叫做 “Miles Per Gallon”,所以在创建header panel的时候我们把它设置为标题。其他panel到目前为止还是空的。

我们来定义一个简单的服务端实现。我们调用shinyServer并传递给它一个函数,用来接收两个参数:inputoutput

server.R

library(shiny)

# Define server logic required to plot various variables against mpg
shinyServer(function(input, output) {

})

服务端程序现在还是空的,不过之后我们会用它来定义输入和输出的关系。

我们来创建一个最小的Shiny应用程序。你可以调用runApp函数来运行这个程序:

> library(shiny)
> runApp("~/shinyapp")

如果一切正常,你会在浏览器里看到如下图所示的应用程序:

MPG Screenshot

我们创建了一个可运行的shiny程序,尽管它还做不了什么。 下一节,我们会完善用户接口并实现服务端脚本,来完成这个应用程序。

输入和输出

我们要使用R内置的datasets包中的mtcars数据构建程序, 允许用户查看箱线图来研究英里每加仑(miles-per-gallon,简称MPG) 和其他三个变量(气缸,变速器,齿轮)之间的关系。

我们想提供一种方式来选择绘制MPG与哪个变量的图形,也提供了个选项,可选择绘图时包含或剔除异常值。为了完成这个目标,我们要往sidebar上加两个元素,一个是selectInput,用来指定变量,另一个是checkboxInput,用来控制是否显示异常值。添加这些元素后的用户接口定义如下所示:

ui.R

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()
))

如果在做了这些修改之后你再运行该程序,你会在sidebar看到两个用户输入:

MPG Screenshot

创建服务端脚本

我们需要定义程序的服务端脚本,用来接收输入,并计算输出。文件server.R如下所示,它说明了下面几个重要的概念:

  • 使用input对象的组件来访问输入,并通过向output对象的组件赋值来生成输出。
  • 在启动的时候初始化的数据可以在应用程序的整个生命周期中被访问
  • 使用反应表达式来计算被多个输出共享的值

shiny服务端的脚本的基本任务是定义输入和输出之间的关系。脚本访问输入值,然后计算,接着向输出的组件赋以反应表达式。

下面是全部服务端脚本的代码(注释更详尽地讲解了实现技巧):

server.R

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)
  })
})

shiny用renderTextrenderPlot生成输出(而不是直接赋值),这样做让程序成为反应式的。这一层封装返回特殊的表达式,只有当其所依赖的值改变的时候才会重新执行。这就使shiny在输入值发生改变时自动更新输出。

展示输出

服务端脚本给两个输出赋值:output$captionoutput$mpgPlot。为了让用户接口能显示输出,我们需要在主UI面板上添加一些元素。

在下面修改后的用户接口定义中,你可以看到,我们用h3元素添加了说明文字,并用textOutput函数添加了其中的文字,还调用了plotOutput函数渲染了图形。

ui.R

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")
  )
))

运行应用程序,就可以显示它的最终形式,包括输入和动态更新的输出:

MPG Screenshot

现在我们完成了一个简单的程序,接下来可能想做一些修改。下一个主题将介绍编辑、运行、调试shiny程序的流程。

运行和调试

在整个教程中,你会发现我们都是调用runApp来运行例子程序。 这个函数会启动shiny程序并打开浏览器以便查看程序。在shiny程序运行的过程中,R控制台的交互将被阻断,也就是说,不能在控制台运行命令。

要停止shiny程序,你只需中断R,有两种方式:(1)在R的任何前端里按下Escape键;(2)点击R环境里提供的停止按钮。

在独立进程里运行

如果你不想在运行shiny程序的时候阻断访问控制台,则可以在单独的进程中运行shiny程序。你可以打开终端窗口或者控制台窗口,然后执行下面的命令:

R -e "shiny::runApp('~/shinyapp')"

默认情况下, runApp 会打开8100端口。如果你用的是默认设置,你可以在浏览器里通过地址http://localhost:8100 来连接应用程序。

下面我们会讨论调试shiny程序的技术,包括停止执行程序和查看当前环境变量的能力。为了把这些技术和在独立的终端会话相结合,你必须用交互方式运行R(也就是,首先输入“R”来启动R会话然后在会话中执行runApp)。

实时重载

当你修改用户接口定义或者服务端脚本的时候,你不必关闭并重启应用程序以查看更改后的效果。只需保存更改,重新加载浏览器就可以查看更新后的程序。

有个条件是这样的:当浏览器重新加载,会引发shiny检查ui.R和server.R的时间戳,以判断这两个文件是否需要重新加载。如果其他脚本或数据文件发生改变,shiny是不会知道的,所以这时要关闭并重启应用程序来查看相应的更改。

调试技巧

打印

有好几种技巧可以用来调试shiny程序。第一种是增加cat函数的调用,这样可以在适当的地方打印诊断信息。例如,下面两条调用就是用来打印标准输出和标准错误的信息:

cat("foo\n")
cat("bar\n", file=stderr())

使用调试浏览器

第二种方式是增加browser函数的显式调用,来中断程序执行,并查看调用browser时所处的环境。注意,使用browser需要你从交互式会话中启动应用程序(这与上面提到的用R -e的方式相反)。

例如,在代码的某个地方无条件地停止执行:

# Always stop execution here
browser() 

你也可以用这种方法在特定条件下停止执行代码。例如,当用户选择”Transmission”作为变量的时候停止MPG程序:

# Stop execution when the user selects "am"
browser(expr = identical(input$variable, "am"))

建立一个自定义错误处理器

你可以设置R的 "error" 选项,使得当错误发生的时候,自动进入调试浏览器:

# Immediately enter the browser when an error occurs
options(error = browser)

另一种方法,你可以设置recover函数做为错误处理器,它可以打印一个调用列表,并允许你在堆栈的任何位置查看:

# Call the recover function when an error occurs
options(error = recover)

如果你想自动地对每个会话设置error选项,你可以用R Startup这篇文章描述的方法来修改.Rprofile文件。

Sliders Screenshot

sliders这个例子展示的是滑动条(slider)控件的功能,包括产生动画效果。要运行这个例子,只需键入:

> library(shiny)
> runExample("05_sliders")

定制化滑动条

slider控件功能非常强大,同时也可以定制化。它支持的特性有:

  • 既可以输入单个的值,也可以输入一个范围。
  • 自定义显示值的格式(比如,货币格式)
  • 让滑动条在一定范围内自动滑动

滑动条控件是由调用sliderInput函数生成的。ui.R文件展示的是多种选项的滑动条。

ui.R

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")
  )
))

服务端脚本

slider应用程序的服务端是很简单的:它创建了个数据框,用来存放所有输入值,然后把它渲染到HTML表中:

server.R

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()
  })
})

Tabsets Screenshot

示例程序Tabsets展示的是如何用选项卡(tabs)来组织输出。要运行这个例子,就执行下面的命令:

> library(shiny)
> runExample("06_tabsets")

选项卡面板(Tab Panels)

选项卡(tabsets)是由调用tabsetPanel函数创建的,在这函数中,又需要用tabPanel函数创建选项(tab)列表。每一个选项卡面板是由输出元素组成的,这些元素在选项卡中垂直排列。

在这个例子中,我们修改了原来的Hello Shiny程序,增加了一个摘要和数据表,两者分别渲染到它们各自的选项卡中。下面就是用户接口的代码:

ui.R

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"))
    )
  )
))

选项卡和反应式数据(Reactive Data)

将选项卡引入用户接口的时候,应该强调为共享数据创建反应表达式的重要性。在这个例子中,每个选项卡都提供了对数据集的查看方式。如果对数据集的处理比较费时,那么用户接口的定义可能变得很慢。下面的服务端脚本展示的是如何用反应表达式一次性计算数据,其结果被三个选项卡所共享。

server.R

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())
  })
})

More Widgets Screenshot

More widgets这个应用程序展示的是帮助文本和提交按钮,以及用嵌入HTML元素的方式来自定义格式。要运行例子,请键入:

> library(shiny)
> runExample("07_widgets")

UI增强

在这个例子里,我们更新了Shiny Text程序,增加了控件,调整了格式,特别地:

  • 增加了helpText控件,用以在输入控件旁边添加解释文本。
  • 增加了 submitButton控件,用来表示我们不想让输入和输出进行实时连接,而是想让用户点击按钮后才更新输出。这一特点在当计算过程非常复杂时是很有用的。
  • 在输出面板上增加了 h4 元素(四级标题)。Shiny提供了大量的函数可直接往页面上添加HTML元素,包括标题、段落、链接等等。

下面更新后的用户接口界面的代码:

ui.R

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")
  )
))

服务端脚本

和原来的Shiny Text的程序比,所有的变化都是在用户接口界面部分,服务端脚本保持不变。

server.R

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)
  })
})

Uploading Files Screenshot

有时你希望用户能上传数据到你的应用程序里。shiny使得用户可以很容易地用浏览器上传数据,然后服务端的逻辑可以访问这些数据。

重要注解:

  • 这一特性不支持IE9或更早版本(shiny server也同样不支持它们)
  • 默认情况下,shiny上传的每个文件最大不能超过5MB。你可以通过shiny.maxRequestSize选项来修改这个限制。例如,在server.R的最前面加上 options(shiny.maxRequestSize=30*1024^2),可以把文件大小限制提高到30MB。

要运行这个例子,可键入:

> library(shiny)
> runExample("09_upload")

文件上传控件是ui.R文件中的 fileInput来创建的,访问上传的数据也跟访问其他类型的输入相类似:用input$inputId来引用。fileInput函数的multiple参数取TRUE可以允许用户选择多个文件,而accept参数可以提示用户应用程序希望接收什么样的文件类型。

ui.R

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')
  )
))

server.R

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)
  })
})

上面这个接收了一个文件,然后用read.csv函数读取这个csv文件,最后用表格来展示所有的数据。正如server.R的注释展示的那样,inFile要么是NULL,要么是包含上传文件信息的数据框,每一行对应的一个文件。在本例中,fileInput没有multiple参数,这样它就只有一行。

文件内容可以通过datapath提供的文件名来访问。查看?fileInput获取更多帮助信息。

Downloading Data Screenshot

到目前为止的例子中,输出结果都是直接出现在页面上,比如绘图、表格、文本框。Shiny也提供了下载计算结果文件的特性,这能可以很容易地构建报告系统。

要运行下面的例子,键入:

> library(shiny)
> runExample("10_download")

要定义文件下载的功能,在服务端用downloadHandler使用函数,在UI端使用downloadButtondownloadLink

ui.R

shinyUI(pageWithSidebar(
  headerPanel('Download Example'),
  sidebarPanel(
    selectInput("dataset", "Choose a dataset:", 
                choices = c("rock", "pressure", "cars")),
    downloadButton('downloadData', 'Download')
  ),
  mainPanel(
    tableOutput('table')
  )
))

server.R

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)
    }
  )
})

正如你看的那样,downloadHandler接收参数filename,这个参数告诉浏览器保存时默认的文件名,它可以是简单的字符串,也可以是返回字符串的函数(如上例)。

content参数必须是包含一个参数的函数,这个参数是临时文件的文件名。content函数的作用是往要下载的临时文件里写内容。

filenamecontent参数可以使用反应值或表达式(尽管在上面例子中的filename,参数是实际的函数,filename = paste(input$dataset, '.csv')不会按你想的那样运行,因为当下载处理器定义时,它只计算一次)。

一般来说,只有两个参数你需要。有个可选参数contentType,如果它是NANULL,shiny会基于文件名猜测一个合适的值。如果你想改变这一行为,可以提供一个类型的字符串(比如,"text/plain")。

Dynamic UI

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:

  • The conditionalPanel function, which is used in ui.R and wraps a set of UI elements that need to be dynamically shown/hidden
  • The renderUI 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 UI
  • Use JavaScript to modify the webpage directly.

Let’s take a closer look at each approach.

Showing and Hiding Controls With conditionalPanel

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:

ui.R

# Partial example
selectInput("dataset", "Dataset", c("diamonds", "rock", "pressure", "cars")),
conditionalPanel(
  condition = "output.nrows",
  checkboxInput("headonly", "Only use first 1000 rows"))

server.R

# 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.

Creating Controls On the Fly With renderUI

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.

ui.R

# Partial example
numericInput("lat", "Latitude"),
numericInput("long", "Longitude"),
uiOutput("cityControls")

server.R

# 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.

Use JavaScript to Modify the Page

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:

  • Before making changes to the DOM that may include adding or removing Shiny inputs or outputs, call Shiny.unbindAll().
  • After such changes, call 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.

HTML UI Screenshot

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")

Defining an HTML UI

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:

www/index.html

<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:

  • HTML form elmements (in this case a select list and a number input) are bound to input slots using their name attribute.
  • Output is rendered into HTML elements based on matching their 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.

Server Script

All of the changes from the original Tabsets application were to the user-interface, the server script remains the same:

server.R

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())
  })
})

Scoping

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.

Per-session objects

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()

  # ...
})

Objects visible across all sessions

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.

Global objects

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.

Scope for included R files

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.

Getting Non-Input Data From the Client

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.

Using session$clientData

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 “?”):

server.R

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=", ")
  })
})
ui.R
shinyUI(bootstrapPage(
  h3("URL components"),
  verbatimTextOutput("urlText"),

  h3("Parsed query string"),
  verbatimTextOutput("queryText")
))

This app will display the following:

URL components

Viewing all available values in clientData

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']].

ui.R
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:

All clientData values

Sending Images

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.

About renderPlot()

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:

  • Image files generated by the writePNG() function from the png package.
  • Image files generated by the rgl.snapshot() function, which creates images from 3D plots made with the rgl package.
  • Images generated by an external program.
  • Pre-rendered images.

The solution in these cases is the renderImage() function.

Using renderImage()

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():

server.R

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)
})

ui.r

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().

Structure of the returned list

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.

Sending pre-rendered images with renderImage()

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:

server.R

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.

Using clientData values

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:

server.R

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.

Reactivity Overview

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:

Reactive roles

Reactive sources and endpoints

The simplest structure of a reactive program involves just a source and an endpoint:

Simplest structure

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))
  })
})

Structure of 01_hello

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
  })
})

Structure of Old Faithful example

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.)

Reactive conductors

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 _n_th value in the Fibonacci sequence, as well as the inverse of _n_th 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:

Fibonacci app without conductor

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:

Fibonacci app with conductor

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 })
})

Summary

In this section, we’ve learned about:

  • Reactive sources can signal objects downstream that they need to re-execute.
  • Reactive conductors are placed somewhere in between sources and endpoints on the reactive graph. They are typically used for encapsulating slow operations.
  • Reactive endpoints can be told to re-execute by the reactive environment, and can request upstream objects to execute.
  • Invalidation arrows diagram the flow of invalidation events. It can also be said that the child node is a dependent of or takes a dependency on the parent node.

Implementations of sources, conductors, and endpoints: values, expressions, and observers

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.

  • Reactive values are an implementation of Reactive sources; that is, they are an implementation of that role.
  • Reactive expressions are an implementation of Reactive conductors. They can access reactive values or other reactive expressions, and they return a value.
  • Observers are an implementation of Reactive endpoints. They can access reactive sources and reactive expressions, and they don’t return a value; they are used for their side effects.

Implementations of reactive 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

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.

Reactive expressions

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:

  • accessing a database
  • reading data from a file
  • downloading data over the network
  • performing an expensive computation

Observers

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.

Differences between reactive expressions and observers

Reactive expressions and observers are similar in that they store expressions that can be executed, but they have some fundamental differences.

  • Observers (and endpoints in general) respond to reactive flush events, but reactive expressions (and conductors in general) do not. We’ll learn more about flush events in the next section. If you want a reactive expression to execute, it must have an observer as a descendant on the reactive dependency graph.
  • Reactive expressions return values, but observers don’t.

Execution scheduling

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.

A simple example

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: * All of its descendants in the graph are invalidated. Sometimes for brevity we’ll say that an observer is dirty, meaning that it is invalidated, or clean, meaning that it is not invalidated. * The arrows that have been followed are removed; they are no longer considered descendants, and changing the reactive value again won’t have any effect on them. Notice that the arrows are dynamic, not static.

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.

An app with reactive conductors

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.

Summary

In this section we’ve learned about:

  • Invalidation flags: reactive expressions and observers are invalidated (marked dirty) when their parents change or are invalidated, and they are marked as clean after they re-execute.
  • Arrow creation and removal: After a parent object follows invalidates its children, the arrows will be removed. New arrows will be created when a reactive object accesses another reactive object.
  • Flush events trigger the execution of endpoints. Flush events occur whenever the browser sends data to the server.

Isolation: avoiding dependency

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:

Isolated reactive value

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 _n_th 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.

Deploying Over the Web

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.

Self-hosted Shiny Server

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.

Pros

  • Easiest for your users—only a web browser is required
  • Take advantage of centralized computing resources

Cons

  • Requires server setup and maintenance of a Linux server

RStudio-hosted Shiny Server

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.

Pros

  • Easiest for your users—only a web browser is required
  • No need to run your own server

Cons

  • Code and data must be copied to our servers

Sharing Apps to Run Locally

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:

Gist

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').

Pros

  • Source code is easily visible by recipient (if desired)
  • Easy to run (for R users)
  • Easy to post and update

Cons

  • Code is published to a third-party server

GitHub repository

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.

Pros

  • Source code is easily visible by recipient (if desired)
  • Easy to run (for R users)
  • Very easy to update if you already use GitHub for your project
  • Git-savvy users can clone and fork your repository

Cons

  • Developer must know how to use git and GitHub
  • Code is hosted by a third-party server

Zip File, delivered over the web

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.

Pros

  • Only requires a web server for delivery

Cons

  • To view the source, recipient must first download and unzip it

Zip File, copied to recipient’s computer

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).

Pros

  • Share apps using e-mail, USB flash drive, or any other way you can transfer a file

Cons

  • Updates to app must be sent manually

Package

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.

Pros

  • Publishable on CRAN
  • Easy to run (for R users)

Cons

  • More work to set up
  • Source code is visible by recipient (if not desired)

Building Inputs

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.)

Design the Component

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:

  • Designed to be used from HTML and R: Shiny user interfaces can either be written using R code (that generates HTML), or by writing the HTML directly. A well-designed Shiny input component will take both styles into account: offer an R function for creating the component, but also have thoughtfully designed and documented HTML markup.
  • Configurable using HTML attributes: Avoid requiring the user to make JavaScript calls to configure the component. Instead, it’s better to use HTML attributes. In your component’s JavaScript logic, you can easily access these values using jQuery (or simply by reading the DOM attribute directly).

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.)

Write an Input Binding

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)
Return the Shiny input ID for the element 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)
Return the Shiny value for the element el. This can be any JSON-compatible value.
setValue(el, value)
Set the element to the specified value. (This is not currently used, but in the future we anticipate adding features that will require the server to push input values to the client.)
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.

Register Input Binding

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.

Example

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.

Building Outputs

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.

Server-Side Output Functions

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)
})

Design Output Component Markup

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.

Write an Output Binding

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)
Return the Shiny output ID for the element 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)
Called when a new value that matches this element's ID is received from the server. The function should render the data on the element. The type/shape of the `data` argument depends on the server logic that generated it; whatever value is returned from the R code is converted to JSON using the RJSONIO package.
renderError(el, err)
Called when the server attempts to update the output value for this element, and an error occurs. The function should render the error on the element. err is an object with a message String property.
clearError(el)
If the element el is currently displaying an error, clear it.

Register Output Binding

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.