Disclaimer:
The examples and code snippets in this tutorial are adapted from the official Shiny tutorial by Posit (formerly RStudio). Full credit goes to the original authors — this walkthrough simply restructures and supplements their content for a smoother, section-wise learning experience.
Shiny is an R package that makes it easy to build interactive web
apps directly from R.
It bridges the gap between statistical computing and user-friendly
interfaces, enabling data scientists to create dynamic dashboards and
applications without needing HTML, CSS, or JavaScript knowledge.
Packages required
Every Shiny app has three main
parts:
This is the simplest Shiny app structure. It loads the required library and sets up an empty user interface with no inputs or outputs.
The app runs successfully but displays a blank page since no elements are included in the UI.
Shiny apps are built with a clear separation between UI (user interface) and server (logic). This section focuses on designing the UI using layouts like page_sidebar(), and organizing content using sidebar(), card(), and value_box(). These components help create clean, readable, and interactive interfaces for your app users.
We now add a card() element to the main panel. Cards help group and visually organize content within the interface.
ui <- page_sidebar(
title = "title panel",
sidebar = sidebar("sidebar"),
card("Card content")
)
# Define server logic ----
server <- function(input, output) {
}
# Run the app ----
shinyApp(ui = ui, server = server)
The card appears in the main panel, giving a more polished look. Cards are useful for grouping content like text, outputs, or plots.
Now let’s introduce a value_box() to display key metrics or indicators. These are often used in dashboards to highlight summary stats.
ui <- page_sidebar(
title = "title panel",
sidebar = sidebar("Sidebar"),
value_box(
title = "Value box",
value = 100,
showcase = bsicons::bs_icon("bar-chart"),
theme = "teal"
),
card("Card"),
card("Another card")
)
# Define server logic ----
server <- function(input, output) {
}
# Run the app ----
shinyApp(ui = ui, server = server)
This version displays a sidebar, a value box (with an icon and value), and two cards. It’s a great starting point for dashboards and summary displays.
Widgets are the heart of interactivity in Shiny apps. They allow users to take action — click buttons, check boxes, select values, choose dates, and more. This section explores different types of input widgets that can be added to our user interface to make apps dynamic and user-friendly following the official Shiny tutorial.
A single checkbox input allows users to toggle between TRUE and FALSE. By default, the checkbox is checked.
ui <- page_fluid(
title = "title panel",
card(
card_header("Single checkbox"),
checkboxInput("checkbox", "Choice A", value = TRUE)
)
)
# Define server logic ----
server <- function(input, output) {
}
# Run the app ----
shinyApp(ui = ui, server = server)
Can use a checkbox group to allow users to select multiple options from a list. Here, “Choice 1” is selected by default.
ui <- page_fluid(
title = "title panel",
card(
card_header("Checkbox group"),
checkboxGroupInput(
"checkGroup",
"Select all that apply",
choices = list("Choice 1" = 1, "Choice 2" = 2, "Choice 3" = 3),
selected = 1)
)
)
# Define server logic ----
server <- function(input, output) {
}
# Run the app ----
shinyApp(ui = ui, server = server)
A dropdown (select box) widget helps conserve space while still offering a list of options.
ui <- page_fluid(
title = "title panel",
card(
card_header("Select box"),
style = "overflow: visible;",
selectInput(
inputId = "select",
label = "Select option",
choices = list("Choice 1" = 1, "Choice 2" = 2, "Choice 3" = 3),
selected = 1),
verbatimTextOutput("selected_val")
)
)
# Define server logic ----
server <- function(input, output) {
output$selected_val <- renderText({
paste("You selected:", input$select)
})
}
# Run the app ----
shinyApp(ui = ui, server = server)
Sliders are great for numeric input, either as a single value or a
range.
One slider lets the user pick a number, and the other sets a range.
These can be used for filtering or setting thresholds.
ui <- page_fluid(
title = "title panel",
card(
card_header("Sliders"),
sliderInput(
"slider1",
"Set value",
min = 0,
max = 100,
value = 50
),
sliderInput(
"slider2",
"Set value range",
min = 0,
max = 100,
value = c(25, 75)
)
)
)
# Define server logic ----
server <- function(input, output) {
}
# Run the app ----
shinyApp(ui = ui, server = server)
This widget allows users to pick a start and end date from a calendar
popup.
Once a date range is selected, it becomes available in input$dates.
ui <- page_fluid(
title = "title panel",
card(
card_header("Date range input"),
dateRangeInput("dates", "Select dates")
)
)
# Define server logic ----
server <- function(input, output) {
}
# Run the app ----
shinyApp(ui = ui, server = server)
This is our first fully functional Shiny app! It connects a slider input to a reactive plot, showing how user actions instantly update outputs.
Outputs in Shiny:
To display something in our app—like a plot, table, or map, we need two things:
*Output()
function
to create space for the output (e.g., plotOutput() for a plot).render*()
function to generate the output
(e.g., renderPlot() to draw the plot).For example:
DTOutput()
in
the UI and renderDT()
in the server.plotOutput()
in
the UI and renderPlot()
in the
server.leafletOutput()
in the UI and renderLeaflet()
in the
server.This simple pattern works for most Shiny outputs—just match the Output with the render!
# ---- UI Section ----
# This defines what the app will look like: the layout and input/output elements
ui <- fluidPage(
# Title displayed at the top
titlePanel("My First Shiny App!"),
# Create a layout with a sidebar and a main area
sidebarLayout(
# Sidebar: contains input elements like sliders, textboxes, etc.
sidebarPanel(
sliderInput("num", # Input ID
"Choose a number:", # Label displayed
min = 10, # Minimum value of slider
max = 100, # Maximum value of slider
value = 50) # Default value when the app loads
),
# Main panel: contains outputs like plots, text, tables
mainPanel(
plotOutput("hist") # Output ID for the plot
)
)
)
# ---- Server Section ----
# This defines the logic behind the app — how inputs affect outputs
server <- function(input, output) {
# Create a plot that reacts to user input
output$hist <- renderPlot({
# Generate a histogram of random numbers based on slider value
hist(rnorm(input$num), # input$num accesses the slider value
main = paste("Histogram of", input$num, "Random Numbers"), # Title
col = "steelblue", # Bar color
border = "white") # Border color
})
}
# ---- Run the App ----
# This function launches the app with the defined UI and server
# shinyApp(ui = ui, server = server)
The app displays a slider in the sidebar. As we move the slider, the histogram in the main panel updates in real time based on the number of random values selected.
Wrapping Up
We’ve taken our first steps with Shiny — and that’s something to feel
good about!
From sliders to sidebars, we’re now familiar with the basic building
blocks of interactive R apps.
But guess what?
The fun is not over — it’s just getting
started!
This is only the beginning, and there’s so much more we can do to make
our apps shine.
Get ready for part two: