Would you like to know how to fully customize the layout of your R shiny web applications in a easy way?
Today you will learn how to:
create a simple R Shiny User Interface (UI) Layout;
how to add a sidebar with inputs;
add multiple rows, columns, tabs and pages in R Shiny;
add a theme and fully customize colors and fonts;
and more…
Watch the video:
Load R packages
Before we begin, make sure you have the required packages installed:
install.packages(c("shiny", "tidyverse", "gt", "bslib"))Single Page Layout with Sidebar
Let’s start with the most common Shiny layout pattern - a sidebar layout. This creates a clean interface with controls on the left and main content on the right.
library(shiny)
library(tidyverse)
library(gt)
ui <- fluidPage(
titlePanel("Starwars Dashboard"),
sidebarLayout(
sidebarPanel(
selectInput(
inputId = "character",
label = "Choose a Character:",
choices = unique(starwars$name),
selected = unique(starwars$name)[1],
multiple = TRUE
)
),
mainPanel(
gt::gt_output("characterInfos")
)
)
)
server <- function(input, output) {
output$characterInfos <- gt::render_gt({
dplyr::starwars %>%
filter(name %in% input$character) %>%
gt() %>%
opt_interactive()
})
}
shinyApp(ui = ui, server = server)This layout demonstrates the fundamental structure of a Shiny application:
sidebarPanel: Contains input controls (in this case, a character selector)mainPanel: Displays the main content (an interactive table)Reactive filtering: The table updates automatically based on user selection
The sidebar layout is responsive and automatically adapts to different screen sizes, making it mobile-friendly.
Multi-Row Layouts with Grid System
For more complex interfaces, you’ll need precise control over element positioning. Shiny uses Bootstrap’s 12-column grid system, allowing you to create custom layouts with multiple rows and columns.
ui <- fluidPage(
titlePanel("Starwars Dashboard"),
fluidRow(
column(6, plotOutput("plot1")),
column(6, plotOutput("plot2"))
),
fluidRow(
column(12, gt::gt_output("table"))
)
)
server <- function(input, output) {
output$plot1 <- renderPlot({
starwars %>%
count(hair_color, gender) %>%
ggplot(aes(hair_color, n, fill = gender)) +
geom_col()
})
output$plot2 <- renderPlot({
starwars %>%
count(eye_color, gender) %>%
ggplot(aes(eye_color, n, fill = gender)) +
geom_col()
})
output$table <- gt::render_gt({ starwars })
}
shinyApp(ui = ui, server = server)Key concepts:
fluidRow(): Creates horizontal rowscolumn(width, ...): Defines column width (1-12) and contentBootstrap grid: Total width of 12 columns per row
Responsive design: Automatically stacks columns on smaller screens
In this example: - First row: Two plots side by side (6 columns each) - Second row: One full-width table (12 columns)
Tabbed Interfaces
Tabs are perfect for organizing related content without overwhelming users. They provide a clean way to separate different views or analyses.
ui <- fluidPage(
titlePanel("Starwars Dashboard"),
tabsetPanel(
tabPanel("Plot", plotOutput("plot")),
tabPanel("Summary", verbatimTextOutput("summary")),
tabPanel("Data", gt::gt_output("table"))
)
)
server <- function(input, output) {
output$table <- gt::render_gt({ starwars })
output$summary <- renderPrint({ summary(starwars$height) })
output$plot <- renderPlot({ ggplot(starwars, aes(x = height, fill = gender)) +
geom_density(alpha = 0.5) })
}
shinyApp(ui = ui, server = server)Tab components: - tabsetPanel(): Container for all tabs - tabPanel(title, content): Individual tab with title and content - Content flexibility: Each tab can contain any Shiny output
You can also combine tabs with the grid system by adding fluidRow() and column() within tab panels for even more layout control.
Multi-Page Applications with Navigation
For larger applications, you might need multiple pages. The navbarPage() function creates a professional navigation bar at the top of your application.
ui <- navbarPage(
title = "Starwars Dashboard",
tabPanel("Introduction", p("An app for analyzing Starwars data.")),
tabPanel("Data", gt::gt_output("dataTable")),
navbarMenu("More Analyses",
tabPanel("Height Analysis", plotOutput("heightPlot")),
tabPanel("Mass Analysis", plotOutput("massPlot"))
)
)
server <- function(input, output) {
output$dataTable <- gt::render_gt({ gt(starwars) |> opt_interactive() })
output$heightPlot <- renderPlot({ ggplot(starwars, aes(height)) + geom_histogram() })
output$massPlot <- renderPlot({ ggplot(starwars, aes(mass)) + geom_histogram() })
}
shinyApp(ui = ui, server = server)Navigation features: - navbarPage(): Creates the main navigation structure - tabPanel(): Individual pages in the navigation - navbarMenu(): Dropdown menus for grouping related pages - Professional appearance: Clean, modern navigation bar
Theming with Bootstrap
The bslib package provides easy access to pre-built Bootstrap themes, allowing you to quickly change your application’s appearance.
library(bslib)
ui <- navbarPage(
title = "Starwars Dashboard",
theme = bslib::bs_theme(bootswatch = "cerulean"), # BOOTSWATCH THEME
tabPanel("Introduction", p("An app for analyzing Starwars data.")),
tabPanel("Data", gt::gt_output("dataTable")),
navbarMenu("More Analyses",
tabPanel("Height Analysis", plotOutput("heightPlot")),
tabPanel("Mass Analysis", plotOutput("massPlot"))
)
)
server <- function(input, output) {
output$dataTable <- gt::render_gt({ gt(starwars) |> opt_interactive() })
output$heightPlot <- renderPlot({ ggplot(starwars, aes(height)) + geom_histogram() })
output$massPlot <- renderPlot({ ggplot(starwars, aes(mass)) + geom_histogram() })
}
shinyApp(ui = ui, server = server)Popular Bootswatch themes include: - cerulean (shown above) - flatly - darkly - cosmo - journal - lumen
Custom Color Schemes
For complete control over your application’s appearance, you can define custom colors and styling with bslib.
library(bslib)
ui <- fluidPage(
theme = bslib::bs_theme(bg = "#E8E8E8", fg = "#242424", primary = "#D4B836"),
titlePanel("Starwars Dashboard"),
sidebarLayout(
sidebarPanel(
sliderInput("massInput", "Select Maximum Mass:",
min = min(starwars$mass, na.rm = TRUE),
max = max(starwars$mass, na.rm = TRUE),
value = 40)
),
mainPanel(
plotOutput("massPlot")
)
)
)
server <- function(input, output) {
output$massPlot <- renderPlot({
starwars %>%
filter(mass <= input$massInput) %>%
ggplot(aes(x = name, y = mass)) +
geom_col()
})
}
shinyApp(ui = ui, server = server)Custom theme parameters: - bg: Background color - fg: Foreground (text) color
- primary: Primary accent color for buttons, links, etc. - Additional options: secondary, success, info, warning, danger
Conclusion
You now have the tools to create sophisticated Shiny layouts! Here’s what we covered:
Basic sidebar layouts for simple interfaces
Grid system with rows and columns for precise control
Tabbed interfaces to organize content
Multi-page navigation for complex applications
Theme customization for professional styling
See you in another tutorial!







