Theming Shiny Apps with bslib
Customize Shiny app appearance using bslib's Bootstrap 5 theming system. From quick Bootswatch themes to advanced Sass customization and dynamic color mode switching.
Quick Start
"shiny" preset (recommended starting point):
Bootswatch theme (for a different visual style):
Custom colors and fonts:
Auto-brand from _brand.yml:
If a _brand.yml file exists in your app or project directory, bs_theme() automatically discovers and applies it. No code changes needed. Requires the brand.yml R package.
Theming Workflow
- Start with the
"shiny"preset (default) or a Bootswatch theme close to your desired look - Customize main colors (
bg,fg,primary) - Adjust fonts with
font_google()or other font helpers - Fine-tune with Bootstrap Sass variables via
...orbs_add_variables() - Add custom Sass rules with
bs_add_rules()if needed - Enable
thematic::thematic_shiny()so plots match the theme - Use
bs_themer()during development for interactive preview
Example:
bs_theme()
Central function for creating Bootstrap themes. Returns a sass::sass_bundle() object.
Use bs_theme_update(theme, ...) to modify an existing theme. Use is_bs_theme(x) to test if an object is a theme.
Presets and Bootswatch
The "shiny" preset (recommended): bs_theme() defaults to preset = "shiny" for Bootstrap 5+. This is a polished, purpose-built theme designed specifically for Shiny apps — it is not plain Bootstrap. It provides professional styling with well-chosen defaults for cards, sidebars, value boxes, and other bslib components. Start here and customize with colors and fonts before reaching for a Bootswatch theme.
Vanilla Bootstrap: Use preset = "bootstrap" to remove the "shiny" preset and get unmodified Bootstrap 5 styling.
Built-in presets: builtin_themes() lists bslib's own presets.
Bootswatch themes: bootswatch_themes() lists all available Bootswatch themes. Choose one that fits the app's purpose and audience — don't apply one by default.
Popular options: "zephyr" (light, modern), "cosmo" (clean), "minty" (fresh green), "flatly" (flat design), "litera" (crisp), "darkly" (dark), "cyborg" (dark), "simplex" (minimalist), "sketchy" (hand-drawn).
Main Colors
The most influential colors — changing these affects hundreds of CSS rules via variable cascading:
Color tips:
bg/fg: similar hue, large luminance difference (ensure contrast for readability)primary: contrasts with bothbgandfg; used for hyperlinks, navigation, input focus- Colors can be any format
htmltools::parseCssColors()understands
Typography
Three font arguments: base_font, heading_font, code_font. Use font_scale to uniformly scale all font sizes (e.g., 1.5 for 150%).
Each argument accepts a single font, a font_collection(), or a character vector of font names.
font_google()
Downloads and caches Google Fonts locally (local = TRUE by default). Internet needed only on first download.
With variable weights: font_google("Crimson Pro", wght = "200..900")
With specific weights: font_google("Raleway", wght = c(300, 400, 700))
Recommend fallbacks to avoid Flash of Invisible Text (FOIT) on slow connections:
Font pairing resource: fontpair.co
font_link()
CSS web font interface for custom font URLs:
font_face()
For locally hosted font files with full @font-face control:
font_collection()
Combine multiple fonts with fallback order:
Low-Level Theming Functions
For customizations beyond bs_theme()'s named parameters. These work directly with Bootstrap's Sass layers.
bs_add_variables()
Add or override Bootstrap Sass variable defaults:
The .where parameter controls placement in the Sass compilation order:
Referencing Bootstrap variables:
bs_add_rules()
Add custom Sass/CSS rules that can reference Bootstrap variables and mixins:
From external file: bs_add_rules(sass::sass_file("www/custom.scss"))
Available Sass functions: lighten(), darken(), mix(), rgba(), color-contrast().
Available Bootstrap mixins: @include media-breakpoint-up(), @include box-shadow(), @include border-radius().
bs_add_functions() and bs_add_mixins()
Add custom Sass functions or mixins to the theme bundle:
bs_bundle()
Append sass::sass_bundle() objects to a theme (for packaging reusable theme extensions):
Bootstrap Sass Variables
Pass any Bootstrap 5 Sass variable through bs_theme(...) or bs_add_variables().
Finding variable names: https://rstudio.github.io/bslib/articles/bs5-variables/
Common variables:
Values can be Sass expressions referencing variables, functions, and math.
Bootstrap CSS Custom Properties
See sass-and-css-variables.md [blocked] for details on:
- How Sass variables compile into
--bs-*CSS custom properties - Runtime vs compile-time variable layers
- How Bootstrap 5.3 color modes use CSS variable overrides
- Per-element theming with
data-bs-theme - CSS utility classes for one-off styling
Dark Mode and Color Modes
See dark-mode.md [blocked] for details on:
- Bootstrap 5.3's client-side color mode system (
data-bs-themeattribute) input_dark_mode()andtoggle_dark_mode()for user-controlled switching- Server-side theme switching with
session$setCurrentTheme() - Writing custom Sass that works across light/dark modes
- Component compatibility (what responds to theming, what doesn't)
Theming R Plots
bs_theme() only affects CSS. R plot output (rendered server-side as images) won't auto-match. Use the thematic package:
- Works with base R, ggplot2, and lattice
- Translates CSS colors into R plotting defaults
font = "auto"also matches fonts frombs_theme()- Complements
bs_themer()for real-time preview
Set global ggplot2 theme for further consistency:
Dashboard Background Styling
The bslib-page-dashboard CSS class adds a light gray background behind the main content area, giving dashboard-style apps a polished look where cards stand out against the background. This is a theming detail — it doesn't change layout behavior, only the visual treatment.
For page_sidebar() dashboards:
For page_navbar() with dashboard-focused pages:
Apply the class to individual nav_panel() containers (not page_navbar() itself) so only dashboard-oriented pages get the gray background:
Interactive Theming Tools
bs_theme_preview()
Standalone demo app for previewing a theme with many example UI components:
Includes the theming UI by default (with_themer = TRUE).
run_with_themer()
Run an existing Shiny app with the theme editor overlay (instead of shiny::runApp()):
bs_themer()
Add the theme editor to your own app's server function:
All three tools print the resulting bs_theme() code to the R console for easy copy-paste. Limitations: Bootstrap 5+ only, Shiny apps and runtime: shiny R Markdown only, doesn't affect 3rd-party widgets that don't use bs_dependency_defer().
Theme Inspection
Retrieve computed Sass variable values:
Check contrast (for accessibility):
Aim for WCAG AA compliance: 4.5:1 for normal text, 3:1 for large text.
Best Practices
- Prefer
bs_theme()over custom CSS -- variables cascade to all related components automatically - Pin Bootstrap version:
bs_theme(version = 5)prevents breakage if defaults change - Use fallback fonts with
font_collection()to avoid FOIT on slow connections - Test across components: inputs, buttons, cards, navs, plots, tables, modals, toasts, mobile
- Check accessibility with
bs_get_contrast()and browser dev tools - Use CSS utility classes for one-off styling instead of custom CSS (see sass-and-css-variables.md [blocked])
- Organize complex themes in a separate
theme.R:
Reference Files
- sass-and-css-variables.md [blocked] -- Bootstrap's two-layer variable system, CSS custom properties, utility classes
- dark-mode.md [blocked] -- Color modes, dark mode, dynamic theming, component compatibility

