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
...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:
theme <- bs_theme(preset = "minty") |>
bs_theme_update(
primary = "#1a9a7f",
base_font = font_google("Lato")
) |>
bs_add_rules("
.card { box-shadow: 0 2px 8px 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 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.
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: 1px 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().