Week 6: Introduction to Shiny and Reactive Programming- Week 7: Building Interactive UI Components
- Week 8: Linking
ggplot2with Shiny for Dynamic Visualisation - Week 9: Creating and Deploying Dashboards
ggplot2 with Shiny for Dynamic VisualisationBy the end of this session (and your own reading), you should be able to \(\dots\)
Download exercises from Week 7 folder on NOW and move them into your R-project directory.
library(shiny)
ui <- fluidPage(
sliderInput(inputId = "num",
label = "Choose a number",
min = 1,
max = 100,
value = 50),
textOutput("result")
)
server <- function(input, output) {
output$result <- renderText({
paste("You chose", input$num)
})
}
shinyApp(ui, server)
Anatomy of Shiny Apps
Tips
reactlog::reactlog_enable() to debugShort cuts
shinyappFile > New File > Shiny Web App...Cmd/Ctrl + Shift + Enterui <- fluidPage( ... )
selectInput(): drop-down-menusliderInput()checkboxInput(): on / offtextInput()dateInput()radioButtons(): one optioncheckboxGroupInput(): multiple optionsrenderUI() and uiOutput()inputId – is the identifier: connects front end with back end
input$favnum for accesslabel a human-readable label for control.value e.g. default valuesliderInput(inputId = "favnum", label = "Fav number", value = 50, min = 0, max = 100)
Check documentation of input:
?sliderInput
What kind of inputs are allowed here?
ui <- fluidPage(
textInput("name", "What's your name?"),
passwordInput("password", "What's your password?"),
textAreaInput("story", "Tell me about yourself", rows = 3)
)
What kind of inputs are allowed here?
ui <- fluidPage(
numericInput("num", "Number one", value = 0, min = 0, max = 100),
sliderInput("num2", "Number two", value = 50, min = 0, max = 100),
sliderInput("rng", "Range", value = c(10, 20), min = 0, max = 100)
)
What kind of inputs are allowed here?
ui <- fluidPage(
dateInput("dob", "When were you born?"),
dateRangeInput("holiday", "When do you want to go travelling next?")
)
What kind of inputs are allowed here?
animals <- c("dog", "cat", "mouse", "bird", "other")
ui <- fluidPage(
selectInput("state", "What's your favourite state?", state.name),
radioButtons("animal", "What's your favourite animal?", animals), # one selection only
checkboxGroupInput("animal", "What animals do you like?", animals), # multiple selections allowed
checkboxInput("restart", "Restart?") # yes / no questions
)
selectInput() can allow multiple selections (multiple = TRUE).
What kind of input is allowed here?
ui <- fluidPage(
fileInput("upload", NULL)
)
What kind of input is allowed here?
ui <- fluidPage(
actionButton("click", "Click me!")
)
Should be paired with observeEvent() or eventReactive() in your server function.
| Function | Input Type | Description | Example Use Case |
|---|---|---|---|
textInput() |
Text | Single-line text input | Enter a name or keyword |
textAreaInput() |
Text | Multi-line text input | Enter a paragraph or comment |
passwordInput() |
Text (masked) | Text input with hidden characters | Enter a password |
numericInput() |
Numeric | Numeric input with optional min/max/step | Set a threshold or value |
sliderInput() |
Numeric (range) | Slider for selecting a value or range | Choose value or range of values |
dateInput() |
Date | Calendar input for selecting a single date | Pick a birthdate or deadline |
dateRangeInput() |
Date (range) | Select a start and end date | Filter data by date range |
checkboxInput() |
Boolean | Single checkbox (TRUE/FALSE) |
Enable or disable a feature |
checkboxGroupInput() |
Categorical | Multiple checkboxes for selecting multiple options | Choose multiple preferences |
radioButtons() |
Categorical | Radio buttons for selecting one option | Choose between few options |
selectInput() |
Categorical | Dropdown menu for selecting one option | Choose between many options |
selectizeInput() |
Categorical | Enhanced selectInput() with search/autocomplete |
Searchable dropdown for long lists |
fileInput() |
File | Upload a file from the user’s computer | Upload CSV or image file |
actionButton() |
Button | Triggers an action when clicked | Submit form or refresh plot |
actionLink() |
Button (link style) | Like actionButton() but styled as a hyperlink |
Trigger UI change or navigation |
colorInput() |
Colour | Colour picker widget | Choose a colour for a plot or theme |
ui <- fluidPage(
numericInput("num", "Number",
value = 0,
min = 0,
max = 100),
textOutput("out")
)
server <- function(input, output) {
output$out <- renderText({
str_c("This is my number: ", input$num)
})
}
input$<id> in the server to access the value of any input widget.render... for reactive behaviour.observeEvent() or reactive() for dynamic behavior....Output to display information.Review inputs.R
value argument?renderTexttextOutput.ui <- fluidPage( )
Nesting layouts and organising content
fluidPage()sidebarLayout()navbarPage()tabsetPanel()conditionalPanel()ui <- fluidPage(
sidebarLayout(
)
)
Nesting layouts and organising content
fluidPage()sidebarLayout()navbarPage()tabsetPanel()conditionalPanel()ui <- fluidPage(
sidebarLayout(
sidebarPanel(
),
mainPanel(
)
)
)
Nesting layouts and organising content
fluidPage()sidebarLayout()navbarPage()tabsetPanel()conditionalPanel()ui <- navbarPage( # top-level navigation bar.
)
Nesting layouts and organising content
fluidPage()sidebarLayout()navbarPage()tabsetPanel()conditionalPanel()ui <- navbarPage( # top-level navigation bar.
tabPanel( # defines a separate view.
)
)
Nesting layouts and organising content
fluidPage()sidebarLayout()navbarPage()tabsetPanel()conditionalPanel()ui <- navbarPage( # top-level navigation bar.
tabPanel( # defines a separate view.
sidebarLayout(
)
)
)
Nesting layouts and organising content
fluidPage()sidebarLayout()navbarPage()tabsetPanel()conditionalPanel()ui <- navbarPage( # top-level navigation bar.
tabPanel( # defines a separate view.
sidebarLayout(
sidebarPanel(
),
mainPanel (
)
)
)
)
Nesting layouts and organising content
fluidPage()sidebarLayout()navbarPage()tabsetPanel()conditionalPanel()fluidPage() is often the top-level layout function in a Shiny app.navbarPage() and tabsetPanel() both use tabs, but:
navbarPage() is for top-level navigation (like switching between app sections).tabsetPanel() is for in-page tabbing (like switching between views within a section).conditionalPanel() is the only one that uses JavaScript expressions for dynamic visibility.| Function | Purpose | Behavior / Structure | Special Features |
|---|---|---|---|
fluidPage() |
Main layout container | Arranges UI elements in a fluid, responsive grid system | Adapts to screen size; often the top-level UI function |
sidebarLayout() |
Two-column layout with sidebar and main area | Combines sidebarPanel() and mainPanel() for classic dashboard-style layout |
Good for apps with controls on the side and outputs in the main area |
navbarPage() |
Multi-page navigation with tabs | Creates a navigation bar with multiple tabPanel() or navbarMenu() items |
App-wide navigation |
tabsetPanel() |
Tabbed content within a page | Displays one tabPanel() at a time; tabs are shown inline (not in navbar) |
Can be nested |
conditionalPanel() |
Conditional visibility of UI elements | Shows/hides UI elements based on JavaScript condition (e.g., input.x == "yes") |
Highly dynamic; useful for reactive UI based on user input |
Review 1_app.R of the week 7 exercises
Task 1: Add a sidebar layout sidebarLayout() (example in 2a_app.R)
selectInput() for variable choicesplotOutput()Task 2: Create a copy of 1_app.R and create two tabs – i.e. tabPanel() – to show different plots either
navbarPage layout with (example in 2b_app.R).tabsetPanel() (example in 2c_app.R).There are three key techniques for creating dynamic user interfaces:
update: family of functions modifies parameters of input controls.tabsetPanel() conditionally shows and hides parts of the user interface.uiOutput() and renderUI() generates selected parts of the user interface.textInput(), is paired with an update function, e.g. updateTextInput(), that allows you to modify the control after it has been created.ui <- fluidPage(
)
server <- function(input, output) {
}
ui <- fluidPage(
numericInput(inputId = "max",
label = "Maximum",
value = 30),
sliderInput(inputId = "n",
label = "n",
value = 20,
min = 0,
max = 100)
)
server <- function(input, output) {
}
ui <- fluidPage(
numericInput(inputId = "max",
label = "Maximum",
value = 30),
sliderInput(inputId = "n",
label = "n",
value = 20,
min = 0,
max = 100)
)
server <- function(input, output) {
observeEvent(input$max, {
})
}
ui <- fluidPage(
numericInput(inputId = "max",
label = "Maximum",
value = 30),
sliderInput(inputId = "n",
label = "n",
value = 20,
min = 0,
max = 100)
)
server <- function(input, output) {
observeEvent(input$max, {
updateSliderInput(inputId = "n",
max = input$max)
})
}
ui <- fluidPage(
numericInput(inputId = "max",
label = "Maximum",
value = 30),
sliderInput(inputId = "n",
label = "n",
value = 20,
min = 0,
max = 100)
)
server <- function(input, output) {
observeEvent(input$max, {
updateSliderInput(inputId = "n",
max = input$max)
})
}
3_app.R): input (max) controls the max of slider.observeEvent() triggers updateSliderInput() whenever max inputs changes.inputId.ui <- fluidPage(
sliderInput(inputId = "x1", label = "x1", value = 0, min = -10, max = 10),
actionButton(inputId = "reset", label = "Reset")
)
server <- function(input, output) {
observeEvent(input$reset, {
updateSliderInput(inputId = "x1", value = 0)
})
}
4_app.R).ui <- fluidPage(
selectInput("mod", "Modality",
choices = mods),
selectInput("ppt", "Participants",
choices = NULL),
plotOutput("plot")
)
server <- function(input, output) {
}
ui <- fluidPage(
selectInput("mod", "Modality",
choices = mods),
selectInput("ppt", "Participants",
choices = NULL),
plotOutput("plot")
)
server <- function(input, output) {
sn_mod <- reactive({
filter(spellname,
modality == input$mod)
})
}
ui <- fluidPage(
selectInput("mod", "Modality",
choices = mods),
selectInput("ppt", "Participants",
choices = NULL),
plotOutput("plot")
)
server <- function(input, output) {
sn_mod <- reactive({
filter(spellname,
modality == input$mod)
})
observeEvent(sn_mod(), {
})
# [...]
}
ui <- fluidPage(
selectInput("mod", "Modality",
choices = mods),
selectInput("ppt", "Participants",
choices = NULL),
plotOutput("plot")
)
server <- function(input, output) {
sn_mod <- reactive({
filter(spellname,
modality == input$mod)
})
observeEvent(sn_mod(), {
choices <- unique(sn_mod()$ppt_id)
})
# [...]
}
ui <- fluidPage(
selectInput("mod", "Modality",
choices = mods),
selectInput("ppt", "Participants",
choices = NULL),
plotOutput("plot")
)
server <- function(input, output) {
sn_mod <- reactive({
filter(spellname,
modality == input$mod)
})
observeEvent(sn_mod(), {
choices <- unique(sn_mod()$ppt_id)
updateSelectInput(inputId = "ppt",
choices = choices)
})
# [...]
}
See 5_app.R: creates a user interface with two select boxes and one output plot.
choices = NULL).The plot in 5_app.R compares familiarised and not familiarsed responses for the same image.
Task: Instead of filtering participants and plotting image names,
sidebarLayout with input controls in the side panel and the plot in the main panel.Start by creating a copy of 5_app.R; then change the code.
6_app.R: Selectively show and hide parts of the UI.updateTabsetPanel() to switch tabs from the server.6_app.R.7_app.R: simulate from the normal and exponential distributions.tabPanel().tabsetPanel().nui <- fluidPage(
selectInput("type", "type",
c("slider", "numeric")),
uiOutput("numeric")
)
server <- function(input, output) {
}
See 8a_app.R
uiOutput() inserts a placeholder in your ui.renderUI() is called within server() to fill in the placeholder with dynamically generated UI.selectInput!For an application see 8b_app.R which changes input method depending on variable type.
ui <- fluidPage(
selectInput("type", "type",
c("slider", "numeric")),
uiOutput("numeric")
)
server <- function(input, output) {
output$numeric <- renderUI({
})
}
See 8a_app.R
uiOutput() inserts a placeholder in your ui.renderUI() is called within server() to fill in the placeholder with dynamically generated UI.selectInput!For an application see 8b_app.R which changes input method depending on variable type.
ui <- fluidPage(
selectInput("type", "type",
c("slider", "numeric")),
uiOutput("numeric")
)
server <- function(input, output) {
output$numeric <- renderUI({
if (input$type == "slider") {
} else {
}
})
}
See 8a_app.R
uiOutput() inserts a placeholder in your ui.renderUI() is called within server() to fill in the placeholder with dynamically generated UI.selectInput!For an application see 8b_app.R which changes input method depending on variable type.
ui <- fluidPage(
selectInput("type", "type",
c("slider", "numeric")),
uiOutput("numeric")
)
server <- function(input, output) {
output$numeric <- renderUI({
if (input$type == "slider") {
sliderInput("dynamic",
"Your choice",
value = 0,
min = 0, max = 10)
} else {
})
}
See 8a_app.R
uiOutput() inserts a placeholder in your ui.renderUI() is called within server() to fill in the placeholder with dynamically generated UI.selectInput!For an application see 8b_app.R which changes input method depending on variable type.
ui <- fluidPage(
selectInput("type", "type",
c("slider", "numeric")),
uiOutput("numeric")
)
server <- function(input, output) {
output$numeric <- renderUI({
if (input$type == "slider") {
sliderInput("dynamic",
"Your choice",
value = 0,
min = 0, max = 10)
} else {
numericInput("dynamic",
"Your choice",
value = 0,
min = 0, max = 10)
}
})
}
See 8a_app.R
uiOutput() inserts a placeholder in your ui.renderUI() is called within server() to fill in the placeholder with dynamically generated UI.selectInput!For an application see 8b_app.R which changes input method depending on variable type.
Update UI filter (sliderInput()) depending on which variable was selected.
ui <- fluidPage(
selectInput("var", "Choose variable:", choices = names(spellname)),
uiOutput("filterUI"),
)
server <- function(input, output){
# Dynamically generate filter input based on selected variable
output$filterUI <- renderUI({
req(input$var)
data <- spellname[[input$var]]
rng <- range(data)
sliderInput("filterVal", paste("Filter", input$var), min = rng[1], max = rng[2], value = rng)
})
}
Base R
data[input$var > input$varRange[1] & input$var < input$varRange[2],]
tidyverse functions need !!sym(input$var) as in
filter(data,
!!sym(input$var) > input$varRange[1] &
!!sym(input$var) < input$varRange[2])
9_app.Rpalmerpenguins::penguinsWickham, H. (2021). Mastering shiny. O’Reilly Media.