Introduction to R Shiny

Lokesh Mano

NBIS, SciLifeLab

14-Apr-2025

Contents

What is shiny?

App structure

  • UI Layout
  • UI Inputs (Widgets)
  • UI Outputs
  • Renderer
  • Builder

App organization

One file format

app.r
ui <- fluidPage()
server <- function(input,output) {}
shinyApp(ui=ui,server=server)

Two file format

ui.r
ui <- fluidPage()
server.r
server <- function(input,output) {}

App execution

  • Run shiny::runApp("path/to/folder")
  • Use shinyApp()
shinyApp(
  ui=fluidPage(),
  server=function(input,output) {}
)
  • Running as a separate process from terminal
R -e "shiny::runApp('~/shinyapp')"
  • From Rmd file using rmarkdown::run()

User Interface (UI)

UI

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
shinyApp(
ui=fluidPage(
  titlePanel("Title Panel"),
  sidebarLayout(
    sidebarPanel(
      helpText("Sidebar Panel")
    ),
    mainPanel(
      "hello"
    )
  )
),
server=function(input,output) {})

Language of the web: HTML, CSS & Javascript

Hypertext Markup Language (HTML)

  • All UI code is translated to HTML
  • Inspect with browser
sidebarPanel(helpText("Sidebar Panel"))
<div class="col-sm-4">
  <form class="well" role="complementary">
    <span class="help-block">Sidebar Panel</span>
  </form>
</div>

Cascading style sheet (CSS)

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
shinyApp(
ui=fluidPage(
  titlePanel("Title Panel"),
  sidebarLayout(sidebarPanel(helpText("Sidebar Panel")),
    mainPanel(tabsetPanel(
                tabPanel("tab1",
                         fluidRow(
                           column(6,helpText("Col1")),
                           column(6,
                                  helpText("Col2"),
                                  fluidRow(
                                    column(4,style="background-color:#b0c6fb",helpText("Col1")),
                                    column(4,style="background-color:#ffa153",helpText("Col2")),
                                    column(4,style="background-color:#b1f6c6",helpText("Col3"))
                                  )
                           )
                         )
                ),
                tabPanel("tab2",
                         inputPanel(helpText("Input Panel"))
                ),
                tabPanel("tab3",
                  wellPanel(helpText("Well Panel"))
                )
              )
    )
  )
),
server=function(input,output) {})

UI • Widgets • Input

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]

shinyApp(
ui=fluidPage(
  fluidRow(
    column(4,
           selectInput("select-input",
                       label="selectInput",
                       choices=c("A","B","C")),
    )
  )
),
server=function(input,output) {
})
?selectInput
selectInput(inputId, label, choices, selected = NULL, multiple = FALSE, 
            selectize = TRUE, width = NULL, size = NULL
)
  • Input ID has to be unique

UI • Widgets • Input

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]

shinyApp(
ui=fluidPage(
  fluidRow(
    column(4,
           fileInput("file-input","fileInput:"),
           selectInput("select-input",label="selectInput",choices=c("A","B","C")),
           sliderInput("slider-input",label="sliderInput",value=5,min=1,max=10),
           numericInput("numeric-input",label="numericInput",value=5,min=1,max=10),
           textInput("text-input",label="textInput"),
           textAreaInput("text-area-input",label="textAreaInput"),
           dateInput("date-input",label="dateInput"),
           dateRangeInput("date-range-input",label="dateRangeInput"),
           radioButtons("radio-button",label="radioButtons",choices=c("A","B","C"),inline=T),
           checkboxInput("checkbox","checkboxInput",value=FALSE),
           actionButton("action-button","Action"),
           hr(),
           submitButton()
    )
  )
),
server=function(input,output) {
})

Widgets gallery

UI • Widgets • Input Functions

Function Description
checkboxInput() Checkbox
checkboxGroupInput() Checkbox group
radioButtons() Radio buttons
dateInput() Single date
dateRangeInput() Date range
fileInput() Upload file
numericInput() Input number
sliderInput() Input number
Function Description
textInput() Single line text input
textAreaInput() Multi-line text input
passwordInput() Password input
selectInput() Dropdown selection
actionButton() Action button
submitButton() Submit button
tabsetPanel() Tabset panel
navbarPage() Page with navbar

Widgets gallery

UI • Widgets • Outputs

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
shinyApp(
ui=fluidPage(
           textInput("text_input",label="textInput",value="hello world"),
           textOutput("text_output")
    ),
server=function(input, output) {
  output$text_output <- renderText({input$text_input})
})
?textOutput
textOutput(outputId)
  • Output ID has to be unique

UI • Widgets • Outputs

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
shinyApp(
ui=fluidPage(fluidRow(column(5,
           textInput("text_input",label="textInput",value="<h3 style='color:red'>Red text</h3>"),
           hr(),
           htmlOutput("html_output"),
           textOutput("text_output"),
           verbatimTextOutput("verbatim_text_output"),
           tableOutput("table_output"),
           plotOutput("plot_output",width="300px",height="300px")
    ))),

server=function(input, output) {
  output$html_output <- renderText({input$text_input})
  output$text_output <- renderText({input$text_input})
  output$verbatim_text_output <- renderText({input$text_input})
  output$table_output <- renderTable({iris[1:3,1:3]})
  output$plot_output <- renderPlot({
    plot(iris[,1],iris[,2])
  })
})

UI • Widgets • Output Functions

Output Renderer Description
textOutput() renderText()/renderPrint() Standard text
verbatimTextOutput() renderText()/renderPrint() Monospaced text
htmlOutput() renderText()/renderPrint() HTML text output
plotOutput() renderPlot() Create and display image
imageOutput() renderImage() Display existing image
tableOutput() renderTable() Table output
uiOutput() renderUI() HTML components

A complete app

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
shinyApp(
ui = fluidPage(
  h3("Temperature Converter"),
  numericInput("celsius", "Degrees Celsius:", value = 0),
  textOutput("fahrenheit")
),

server = function(input, output) {
  output$fahrenheit <- renderText({
    paste(input$celsius, "°C is ", (input$celsius * 9/5) + 32, " °F")
  })
})

Dynamic UI

  • UI elements are created conditionally using uiOutput()/renderUI()
shinyApp(
ui=fluidPage(
  selectInput("type",label="Select input type", choices=c("Text","Number")),
  uiOutput("ui"),
  textOutput("text_output"),
),

server=function(input, output) {
  
 output$ui <- renderUI({
    if(input$type=="Text") {
      textInput("input_text","Enter text")
    }else{
      sliderInput("input_number", "Select number", value=5, min=1, max=10)
    }
  })
  
  output$text_output <- renderText({
    if(input$type=="Text") {
      input$input_text
    }else{
      input$input_number
    }
  })
})
  • Other options include conditionalPanel(), insertUI() and removeUI()

Rendering UI

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
shinyApp(
ui=fluidPage(
  selectInput("type", label="Select input type", choices=c("Text","Number")),
  uiOutput("ui"),
  textOutput("text_output"),
),

server=function(input, output) {
  
 output$ui <- renderUI({
    if(input$type=="Text") {
      textInput("input_text", "Enter text", value="hello")
    }else{
      sliderInput("input_number", "Select number", value=5, min=1, max=10)
    }
  })
  
  output$text_output <- renderText({
    if(input$type=="Text") {
      input$input_text
    }else{
      input$input_number
    }
  })
})

Rendering UI

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]

shinyApp(
ui=fluidPage(
  selectInput("data",label="Select data",
              choices=c("mtcars","faithful","iris")),
  tableOutput("table"),
  uiOutput("ui")
),

server=function(input, output) {
  
  data <- reactive({ get(input$data, 'package:datasets') })
  
 output$ui <- renderUI({
  if(input$data=="iris") plotOutput("plot",width="400px")
  })
  
  output$plot <- renderPlot({hist(data()[, 1])})
  output$table <- renderTable({head(data())})
})




Rendering UI

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]

shinyApp(
ui=fluidPage(
  sliderInput("persons",label="Select number of persons", value=1, min=1,max=4),
  uiOutput("ui")
),

server=function(input, output) {
  output$ui <- renderUI({
    lapply(1:input$persons, function(i) {
         div(
           textInput(paste0("name",i),"Enter name:",paste0("Person ",i)),
           textInput(paste0("tel",i),"Enter phone number:",value = "0773921562"),
           hr()
         )
    })
  })
})

Server

  • Server is a function that assembles your input into output using R based code.
  • Three rules to be followed to write a server function:

Rule 1: Save objects to display to output$

Server

Rule 2: Build objects to display with render*()

  • R-Code block (can even be an entire R script) between the braces {} inside the render*() function.
  output$hist <- renderPlot({
                    tile <- "histogram of 100 random numbers"
                    hist(rnorm(100), main = title)
                })

Server

Rule 3: Use input values with input$

Thank you. Questions?

Slide inspirations: Roy Francis (NBIS, RaukR2024)