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): page_sidebar ( theme = bs_theme ( ) ,
"shiny" preset by default — polished, not plain Bootstrap
... ) Bootswatch theme (for a different visual style): page_sidebar ( theme = bs_theme ( preset = "zephyr" ) ,
or "cosmo", "minty", "darkly", etc.
... ) Custom colors and fonts: page_sidebar ( theme = bs_theme ( version = 5 , bg = "#FFFFFF" , fg = "#333333" , primary = "#2c3e50" , base_font = font_google ( "Lato" ) , heading_font = font_google ( "Montserrat" ) ) , ... ) 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. bs_theme ( brand = FALSE )
Disable auto-discovery
bs_theme ( brand = TRUE )
Require _brand.yml (error if not found)
bs_theme ( brand = "path/to/brand.yml" )
Explicit path
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 ... or bs_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: theme <- bs_theme ( preset = "minty" ) |
bs_theme_update ( primary = "#1a9a7f" , base_font = font_google ( "Lato" ) ) |
bs_add_rules ( " .card { box - shadow : 0 2 px 8 px rgba ( 0 , 0 , 0 , 0.1 ) ; } " ) bs_theme() Central function for creating Bootstrap themes. Returns a sass::sass_bundle() object. bs_theme ( version = version_default ( ) , preset = NULL ,
"shiny" (default for BS5+), "bootstrap", or Bootswatch name
... ,
Bootstrap Sass variable overrides
brand
NULL ,
brand.yml: NULL (auto), TRUE (require), FALSE (disable), or path
bg
NULL , fg = NULL , primary = NULL , secondary = NULL , success = NULL , info = NULL , warning = NULL , danger = NULL , base_font = NULL , code_font = NULL , heading_font = NULL , font_scale = NULL ,
Scalar multiplier for base font size (e.g., 1.5 = 150%)
bootswatch
NULL
Alias for preset
- )
- 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:
- Parameter
- Description
- bg
- Background color
- fg
- Foreground (text) color
- primary
- Primary brand color (links, nav active states, input focus)
- secondary
- Default for action buttons
- success
- Positive/success states (typically green)
- info
- Informational content (typically blue-green)
- warning
- Warnings (typically yellow)
- danger
- Errors/destructive actions (typically red)
- bs_theme
- (
- bg
- =
- "#202123"
- ,
- fg
- =
- "#B8BCC2"
- ,
- primary
- =
- "#EA80FC"
- ,
- secondary
- =
- "#48DAC6"
- )
- Color tips:
- bg
- /
- fg
-
- similar hue, large luminance difference (ensure contrast for readability)
- primary
- contrasts with both bg and fg ; 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. bs_theme ( base_font = font_google ( "Roboto" ) , heading_font = font_google ( "Montserrat" ) , code_font = font_google ( "Fira Code" ) ) 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: bs_theme ( base_font = font_collection ( font_google ( "Lato" , local = FALSE ) , "Helvetica Neue" , "Arial" , "sans-serif" ) ) Font pairing resource: fontpair.co font_link() CSS web font interface for custom font URLs: font_link ( "Crimson Pro" , href = "https://fonts.googleapis.com/css2?family=Crimson+Pro:wght@200..900" ) font_face() For locally hosted font files with full @font-face control: font_face ( family = "Crimson Pro" , style = "normal" , weight = "200 900" , src = "url(fonts/crimson-pro.woff2) format('woff2')" ) font_collection() Combine multiple fonts with fallback order: font_collection ( font_google ( "Lato" ) , "Helvetica Neue" , "Arial" , "sans-serif" ) 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: theme <- bs_add_variables ( bs_theme ( preset = "sketchy" , primary = "orange" ) , "body-bg" = "#EEEEEE" , "font-family-base" = "monospace" , "font-size-base" = "1.4rem" , "btn-padding-y" = ".16rem" ) The .where parameter controls placement in the Sass compilation order: .where When to use "defaults" (default) Set variable defaults with !default flag. Placed before Bootstrap's own defaults. "declarations" Reference other Bootstrap variables (e.g., $secondary ). Placed after Bootstrap's defaults. "rules" Placed after all rules. Rarely needed. Referencing Bootstrap variables:
This fails in bs_theme() because $secondary isn't defined yet:
bs_theme("progress-bar-bg" = "$secondary")
Use bs_add_variables with .where = "declarations" instead:
bs_theme ( ) |
bs_add_variables ( "progress-bar-bg" = "$secondary" , .where = "declarations" ) bs_add_rules() Add custom Sass/CSS rules that can reference Bootstrap variables and mixins: theme <- bs_theme ( primary = "#007bff" ) |
bs_add_rules ( " .custom - card { background : mix ( $ bg , $ primary , 95 % ) ; border : 1 px solid $ primary ; padding : $ spacer ; @ include media - breakpoint - up ( md ) { padding : $ spacer * 2 ; } } " ) 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: theme |
bs_add_functions ( "@function my-tint($color) { @return mix(white, $color, 20%); }" ) |
bs_add_rules ( ".highlight { background: my-tint($primary); }" ) bs_bundle() Append sass::sass_bundle() objects to a theme (for packaging reusable theme extensions): my_extension <- sass :: sass_layer ( defaults = list ( "my-var" = "red !default" ) , rules = ".my-class { color: $my-var; }" ) theme <- bs_theme ( ) |
bs_bundle ( my_extension ) 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: bs_theme ( "border-radius" = "0.5rem" , "card-border-radius" = "1rem" , "card-bg" = "lighten($bg, 5%)" , "navbar-bg" = "$primary" , "link-color" = "$primary" , "font-size-base" = "1rem" , "spacer" = "1rem" , "btn-padding-y" = ".5rem" , "btn-padding-x" = "1rem" , "input-border-color" = "#dee2e6" ) Values can be Sass expressions referencing variables, functions, and math. Bootstrap CSS Custom Properties See sass-and-css-variables.md 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 for details on: Bootstrap 5.3's client-side color mode system ( data-bs-theme attribute) input_dark_mode() and toggle_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: library ( thematic ) thematic_shiny ( font = "auto" )
Call before shinyApp()
shinyApp ( ui , server ) Works with base R, ggplot2, and lattice Translates CSS colors into R plotting defaults font = "auto" also matches fonts from bs_theme() Complements bs_themer() for real-time preview Set global ggplot2 theme for further consistency: library ( ggplot2 ) theme_set ( theme_minimal ( ) ) 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: page_sidebar ( class = "bslib-page-dashboard" , title = "My Dashboard" , sidebar = sidebar ( ... ) , ... ) 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: page_navbar ( title = "Analytics" , nav_panel ( "Dashboard" , class = "bslib-page-dashboard" , layout_column_wrap ( ... ) ) , nav_panel ( "Report" ,
No dashboard class — standard white background for prose/reports
... ) ) Interactive Theming Tools bs_theme_preview() Standalone demo app for previewing a theme with many example UI components: bslib :: bs_theme_preview ( )
Default theme
bslib :: bs_theme_preview ( bs_theme ( preset = "darkly" ) )
Custom theme
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() ): run_with_themer ( shinyApp ( ui , server ) ) run_with_themer ( "path/to/app" ) bs_themer() Add the theme editor to your own app's server function: server <- function ( input , output , session ) { bs_themer ( )
Add during development, remove for production
...
- }
- 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:
- vars
- <-
- c
- (
- "body-bg"
- ,
- "body-color"
- ,
- "primary"
- ,
- "border-radius"
- )
- bs_get_variables
- (
- bs_theme
- (
- )
- ,
- varnames
- =
- vars
- )
- bs_get_variables
- (
- bs_theme
- (
- preset
- =
- "darkly"
- )
- ,
- varnames
- =
- vars
- )
- Check contrast (for accessibility):
- bs_get_contrast
- (
- bs_theme
- (
- )
- ,
- c
- (
- "primary"
- ,
- "dark"
- ,
- "light"
- )
- )
- 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 ) Organize complex themes in a separate theme.R :
theme.R
app_theme <- function ( ) { bs_theme ( version = 5 , primary = "#2c3e50" , base_font = font_google ( "Lato" ) , heading_font = font_google ( "Montserrat" , wght = c ( 400 , 700 ) ) ) |
bs_add_rules ( sass :: sass_file ( "www/custom.scss" ) ) } Reference Files sass-and-css-variables.md -- Bootstrap's two-layer variable system, CSS custom properties, utility classes dark-mode.md -- Color modes, dark mode, dynamic theming, component compatibility