R
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Data Structures
-
Section 4: Data Manipulation
-
Section 5: Visualization and Statistics
-
Section 6: Advanced R
-
Section 7: Practical Projects
-
Section 8: Interview Practice
-
Section 9: More Practice Exercises
-
Section 10: Shiny Apps in Depth
-
Section 11: More Data Wrangling
-
Section 12: Tidyverse Deep Dive
-
Section 13: Statistical Modeling Deep Dive
-
Section 14: Machine Learning in R
-
Section 15: R Visualization Deep Dive
-
Section 16: R Package Development Deep Dive
-
Section 17: R for Reproducible Research
-
Section 18: R and Databases
-
Section 19: R Performance Optimization
-
Section 20: Bioinformatics and Specialized R
-
Section 21: More Shiny Practice
-
Section 22: More Practice Exercises
-
Section 23: R Data Structures Deep Dive
-
Section 24: More Interview and Review
-
Section 25: R for Business Analytics
-
Section 26: R Text Mining and NLP
-
Section 27: R Spatial Data Analysis
-
Section 28: R Deep Learning
-
Section 29: Advanced Statistical Techniques
-
Section 30: R Object Systems Deep Dive
-
Section 31: R Environments and Metaprogramming
-
Section 32: R for Finance
-
Section 33: R for Clinical and Health Data
-
Section 34: More Shiny Advanced Practice
-
Section 35: R Data Cleaning Deep Dive
-
Section 36: R Reporting Automation
-
Section 37: More Practical Projects Round 2
-
Section 38: R Ecosystem and Career
-
Section 39: More Visualization Practice
-
Section 40: R for Experimentation
-
Section 41: R for Genomics and Bioinformatics Deep Dive
-
Section 42: R for Marketing Analytics
-
Section 43: R Data Import/Export Deep Dive
-
Section 44: R String Processing Deep Dive
-
Section 45: R for Actuarial and Insurance Analytics
-
Section 46: R Testing and Quality Assurance Deep Dive
-
Section 47: R Version Control and Collaboration
-
Section 48: R Advanced Functional Programming
-
Section 49: R for Supply Chain and Operations
-
Section 50: More Practice Exercises Round 3
-
Section 51: R Dashboards and BI Integration
-
Section 52: R Data Governance and Ethics
-
Section 53: More Modeling Practice
-
Section 54: R Final Capstone Projects
-
Section 55: R for Sports Analytics
-
Section 56: More Interview Practice Round 2
-
Section 57: R Networking and APIs
-
Section 58: R for Environmental Science
-
Section 59: R Notebook and Documentation Practices
-
Section 60: More Data Wrangling Mastery
-
Section 61: R for A/B Testing at Scale
-
Section 62: R Package Ecosystem Deep Dive
36: Building a Simple Shiny Dashboard
You've probably spent most of your time in R writing scripts that run linearly—top to bottom. But once you move into Shiny, you're shifting from a "script" mindset to an "app" mindset. The most frustrating part of this transition is usually the reactivity. I can't tell you how many times I've stared at a blank white screen in my browser, wondering why my plot isn't appearing, only to realize I've made a fundamental mistake in how the UI and Server talk to each other.
Take a look at this snippet. This is a classic "Day One" Shiny bug. The developer is trying to create a simple dashboard to filter the mtcars dataset by cylinder count.
library(shiny)
ui <- fluidPage(
selectInput("cyl_select", "Select Cylinders:", choices = c(4, 6, 8)),
plotOutput("carPlot")
)
server <- function(input, output) {
output$carPlot <- {
# The goal: Plot MPG vs HP for the selected cylinder count
filtered_data <- mtcars[mtcars$cyl == input$cyl_select, ]
plot(filtered_data$hp, filtered_data$mpg, main = "HP vs MPG")
}
}
shinyApp(ui = ui, server = server)
The 'Invisible Plot' and the Missing Renderer
If you run this, the app will load, but the plot area will stay stubbornly empty. There are no errors in the console, which is the worst kind of bug. What's happening here?
In Shiny, the ui defines where things go (the plotOutput), but the server defines how to create them. However, you can't just assign a plot directly to an output object. Shiny doesn't know that the code inside that block is supposed to be a plot that needs to be redrawn whenever the input changes. You've provided the logic, but you haven't provided the renderer.
Wrapping Logic in renderPlot
To fix this, we have to wrap the plotting code in renderPlot({}). This tells Shiny: "Watch the reactive inputs inside this block; whenever input$cyl_select changes, re-execute this code and send the resulting image to the UI."
server <- function(input, output) {
output$carPlot <- renderPlot({
# Now Shiny knows this is a reactive plot
filtered_data <- mtcars[mtcars$cyl == input$cyl_select, ]
plot(filtered_data$hp, filtered_data$mpg,
xlab = "Horsepower", ylab = "MPG",
main = paste("Cars with", input$cyl_select, "Cylinders"))
})
}
Notice how I also added paste() to the title. This is a nice touch for dashboards—it gives the user immediate confirmation that the app is actually responding to their input.
Organizing the Dashboard Layout
Using fluidPage is fine for a quick test, but real dashboards usually need a bit more structure. I generally prefer sidebarLayout because it separates the controls (the "knobs and dials") from the results (the "gauges").
When you use sidebarLayout, you get two main sections: the sidebarPanel and the mainPanel. This keeps your UI from looking like a giant vertical list of widgets. Here is how I would structure a clean, professional version of that same app:
ui <- fluidPage(
titlePanel("Motor Trend Car Research"),
sidebarLayout(
sidebarPanel(
helpText("Adjust the filter to update the plot."),
selectInput("cyl_select", "Cylinders:", choices = c(4, 6, 8))
),
mainPanel(
plotOutput("carPlot")
)
)
)
Understanding the Input-Output Bridge
The most important thing to remember as you build more complex dashboards is the naming convention. The string you put in the UI (like "cyl_select") is the exact key you use in the server (input$cyl_select). If you have a typo in one, the bridge collapses, and your app will either crash or simply stop updating.
I always recommend naming your IDs descriptively. "input1" and "plot1" are fine for a 10-line script, but once you have five filters and three plots, you'll spend half your day hunting for which "input" is which. Use names like "date_range_filter" or "species_dropdown". Your future self will thank you.
📋 Practical Task
Build a Diamond Cut Analysis Dashboard
Using the diamonds dataset (from the ggplot2 package), create a Shiny dashboard that allows a user to explore the relationship between carat weight and price based on the quality of the diamond's cut.
Your dashboard must include:
- A
titlePanelthat clearly names the application. - A
sidebarLayoutcontaining asidebarPaneland amainPanel. - A
selectInputin the sidebar that lets the user choose a cut (Fair, Good, Very Good, Premium, Ideal). - A
plotOutputin the main panel that displays a scatter plot ofcarat(x-axis) vsprice(y-axis). - A server function that uses
renderPlotto filter thediamondsdataset based on the user's selection and render the plot.
Pro Tip: Since the diamonds dataset is quite large, make sure your filtering logic happens inside the renderPlot call so the plot updates reactively!
There are no comments for now.