Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Build modern Shiny dashboards and applications using bslib (Bootstrap 5). Use when creating new Shiny apps, modernizing legacy apps (fluidPage, fluidRow/column, tabsetPanel, wellPanel, shinythemes), or working with bslib page layouts, grid systems, cards, value boxes, navigation, sidebars, filling layouts, theming, accordions, tooltips, popovers, toasts, or bslib inputs. Assumes familiarity with basic Shiny.
.claude/skills/posit-dev-shiny-bslib/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 30% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 64% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 63% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 87% | 0% |
| case-22 | ✗→✓ | ▲ Improved | 132% | 0% |
Build professional Shiny dashboards using bslib's Bootstrap 5 components and layouts. This skill focuses on modern UI/UX patterns that replace legacy Shiny approaches.
Single-page dashboard:
rlibrary(shiny) library(bslib) ui <- page_sidebar( title = "My Dashboard", theme = bs_theme(version = 5), # "shiny" preset by default sidebar = sidebar( selectInput("variable", "Variable", choices = names(mtcars)) ), layout_column_wrap( width = 1/3, fill = FALSE, value_box(title = "Users", value = "1,234", theme = "primary"), value_box(title = "Revenue", value = "$56K", theme = "success"), value_box(title = "Growth", value = "+18%", theme = "info") ), card( full_screen = TRUE, card_header("Plot"), plotOutput("plot") ) ) server <- function(input, output, session) { output$plot <- renderPlot({ hist(mtcars[[input$variable]], main = input$variable) }) } shinyApp(ui, server)
Multi-page dashboard:
rui <- page_navbar( title = "Analytics Platform", theme = bs_theme(version = 5), nav_panel("Overview", overview_ui), nav_panel("Analysis", analysis_ui), nav_panel("Reports", reports_ui) )
page_sidebar() -- Single-page dashboard with sidebar (most common)page_navbar() -- Multi-page app with top navigation barpage_fillable() -- Viewport-filling layout for custom arrangementspage_fluid() -- Scrolling layout for long-form contentSee page-layouts.md for detailed guidance.
layout_column_wrap() -- Uniform grid with auto-wrapping (recommended for most cases)layout_columns() -- 12-column Bootstrap grid with precise controlSee grid-layouts.md for detailed guidance.
Primary container for dashboard content. Support headers, footers, multiple body sections, and full-screen expansion.
See cards.md for detailed guidance.
Display key metrics and KPIs with optional icons, sparklines, and built-in theming.
See value-boxes.md for detailed guidance.
page_navbar() for multi-page appsnavset_card_underline(), navset_tab(), navset_pill() for tabbed contentSee navigation.md for detailed guidance.
page_sidebar() or page_navbar(sidebar = ...)layout_sidebar() within cardsresizable = TRUE by default — users can drag the edge to resize on desktopSee sidebars.md for detailed guidance.
The fill system controls how components resize to fill available space. Key concepts: fillable containers, fill items, fill carriers. Fill activates when containers have defined heights.
See filling.md for detailed guidance.
bs_theme() with Bootswatch themes for quick stylingbg, fg, primary affect hundreds of CSS rulesfont_google() for typographyinput_dark_mode() + session$setCurrentTheme()See theming.md for detailed guidance.
See accordions.md, tooltips-popovers.md, toasts.md, and toolbars.md.
Recommended: bsicons package (Bootstrap Icons, designed for bslib):
rbsicons::bs_icon("graph-up") bsicons::bs_icon("people", size = "2em")
Browse icons: https://icons.getbootstrap.com/
Alternative: fontawesome package:
rfontawesome::fa("envelope")
Accessibility for icon-only triggers: When an icon is used as the sole trigger for a tooltip, popover, or similar interactive element (no accompanying text), it must be accessible to screen readers. By default, icon packages mark icons as decorative (aria-hidden="true"), which hides them from assistive technology.
bsicons::bs_icon(): Provide title — this automatically sets a11y = "sem"r tooltip( bs_icon("info-circle", title = "More information"), "Tooltip content here" )
fontawesome::fa(): Set a11y = "sem" and provide titler tooltip( fa("circle-info", a11y = "sem", title = "More information"), "Tooltip content here" )
The title should describe the purpose of the trigger (e.g., "More information", "Settings"), not the icon itself (e.g., not "info circle icon").
input_switch() -- Toggle switch (modern checkbox alternative)input_dark_mode() -- Dark mode toggleinput_task_button() -- Button for long-running operationsinput_code_editor() -- Code editor with syntax highlightinginput_submit_textarea() -- Textarea with explicit submissionSee inputs.md for detailed guidance.
page_sidebar() (single-page) or page_navbar() (multi-page)bs_theme() (consider Bootswatch for quick start)fill = FALSE on container)layout_column_wrap() or layout_columns()full_screen = TRUE on all visualization cardsthematic::thematic_shiny() for plot themingSee migration.md for a complete mapping of legacy patterns to modern equivalents. Key steps:
fluidPage() with page_sidebar() or page_navbar()fluidRow()/column() with layout_columns()card(full_screen = TRUE)theme = bs_theme(version = 5)value_box() componentstabsetPanel() with navset_card_underline()page_sidebar(), page_navbar(), page_fillable(), page_fluid()) over legacy equivalents (fluidPage(), navbarPage())layout_column_wrap() or layout_columns() for grid layouts instead of fluidRow()/column(), which don't support filling layoutscard(full_screen = TRUE) when building dashboards -- full-screen expansion is a high-value featurefill = FALSE on layout_column_wrap() containers holding value boxes (they shouldn't stretch to fill height)theme = bs_theme(version = 5) or a preset themethematic::thematic_shiny() in the server so base R and ggplot2 plots match the app themewidth = "250px" in layout_column_wrap() for auto-adjusting columnsaccordion() when sidebars have many controlscard() containers. navset_card_*() functions are already cards; nav_panel() content goes directly inside them without wrapping in card()layout_columns() and layout_column_wrap() for laying out multiple elements. Single children should be passed directly to their container functions.page_*() functions. Only use one top-level page function per app.Other measured skills in the registry, with their headline benchmark lift.