Skip to contents

bslib 0.7.0

CRAN release: 2024-03-29

This large release includes many improvements and bug fixes for newer UI components like layout_columns(), card(), and sidebar(). In addition, the new input_task_button() offers a drop-in replacement for shiny::actionButton() (to prevent multiple submissions of the same operation) as well as pairing nicely with the new shiny::ExtendedTask for implementing truly non-blocking operations in Shiny.

New features

  • Added input_task_button(), a replacement for shiny::actionButton() that automatically prevents an operation from being submitted multiple times. It does this by, upon click, immediately transitioning to a “Processing…” visual state that does not let the button be clicked again. The button resets to its clickable state automatically after the reactive flush it causes is complete; or, for advanced scenarios, update_task_button() can be used to manually control when the button resets.

  • Both card() and value_box() now take an id argument that, when provided, is used to report the full screen state of the card or value box to the server. For example, when using card(id = "my_card", full_screen = TRUE) you can determine if the card is currently in full screen mode by reading the boolean value of input$my_card_full_screen. (#1006, #1032)

Changes & improvements

  • For sidebar():

    • The page-level sidebar for page_sidebar()/page_navbar() is now always open (and not collapsible) by default on mobile screens. To revert to the old behavior, set open = "desktop" in the sidebar. (#943)

    • open now accepts a list with mobile and desktop values to control the sidebar’s initial state on each screen size, choosing from "open", "closed", or "always" (for an always-open sidebar that cannot be collapsed). (#943)

    • The collapse toggle now has a high z-index value to ensure it always appears above elements in the main content area. The sidebar overlay also now receives the same high z-index on mobile layouts. (#958)

  • Improved card(full_screen = TRUE, ...) accessibility:

    • Full-screen cards are now supported on mobile devices: the Expand card button is revealed when a user taps on the card (thanks @Damonsoul, #961).

    • The Expand card button is now accessible via keyboard navigation and appropriate ARIA attributes connect the card with the expand and close buttons.

    • For JavaScript-oriented users, the expansion/collapse is now accompanied by a custom bslib.card event with the full screen state reported in the event.detail.fullScreen property. (#959)

  • Improvements to the default theme (i.e., Shiny preset):

    • In the default theme, cards now use a slightly smaller shadow and the same shadow style is also now used by popovers. (#998)

    • Increased spacing between elements. This change is most noticeable in the layout_columns() or layout_column_wrap() component. In these and other components, you can use gap and padding arguments to choose your own values, or you can set the $bslib-spacer (Sass) or --bslib-spacer (CSS) variable. (#998)

  • For layout_columns():

    • col_widths now sets the sm breakpoint by default, rather than the md breakpoint. For example, col_widths = c(12, 6, 6) is now equivalent to breakpoints(sm = c(12, 6, 6)) rather than breakpoints(md = c(12, 6, 6)). (#1014)

    • When col_widths has a breakpoints() at lg or wider, it now uses a better default column width for the smaller breakpoints not listed in the col_widths value. That said, you can always include sm or md in your breakpoints() definition to have complete control over column widths at those sizes. (#931)

    • When row_heights is a non-breakpoints() object, that value is used for the row heights at all breakpoints. Previously, it was used for the row heights from "sm" up. (#931)

    • When an integer value for any breakpoint is provided to col_widths, a 12-unit grid is always used. For example, breakpoints(md = 3, lg = NA) will pick a best-fitting layout for large screen sizes using the 12-column grid. Previously, the best fit algorithm might adjust the number of columns as a shortcut to an easy solution. That shortcut is only taken when an auto-fit layout is requested for every breakpoint, e.g. col_widths = breakpoints(md = NA, lg = NA) or col_widths = NA. (#928)

    • Underlying logic moved from R to Typescript to improve the portability of the component. (#931)

  • value_box(), layout_columns() and layout_column_wrap() now all have min_height and max_height arguments. These are useful in filling layouts, like page_fillable(), page_sidebar(fillable = TRUE) or page_navbar(fillable = TRUE). For example, you can use layout_columns(min_height = 300, max_height = 500) to ensure that a set of items (likely arranged in a row of columns) are always between 300 and 500 pixels tall. (#1016)

  • page_sidebar() now places the title element in a .navbar container that matches the structure of page_navbar(). This ensures that the title elements of page_sidebar() and page_navbar() have consistent appearance. (#998)

  • as_fillable_container(), as_fill_item() and as_fill_carrier() now always include the htmltools fill CSS dependency. This means that they are no longer usable with the $addAttr() htmltools::tagQuery method; authors should instead pass elements to the as_fillable_container() and as_fill_*() functions and use the css_selector argument to apply fill options to specific elements. (#946)

Bug fixes

bslib 0.6.2

CRAN release: 2024-03-22

Increased the version requirement on the sass package to 0.4.9. As a result, font_google(local=TRUE) should no longer fail to download font files.

bslib 0.6.1

CRAN release: 2023-11-28

Bug fixes

  • Fixed the CSS for the bslib-page-dashboard class in the Shiny preset to correctly support page_sidebar(class = "bslib-page-dashboard") and page_navbar(nav_panel(class = "bslib-page-dashboard")). (#917)

  • Fixed a minor bug to allow the themer demo to be run directly, outside of bs_theme_preview(). (#918)

bslib 0.6.0

CRAN release: 2023-11-21

Breaking changes

  • bs_theme() now defaults to preset="shiny". This provides an additional set of theming defaults and rules that make it easier to create Shiny apps (in particular, dashboards) that look good out of the box. To revert to the previous behavior, set bs_theme(preset="bootstrap"). (#711)

  • value_box() no longer defaults to theme_color = "primary". To restore the previous behavior, please use theme = "primary". In addition to the default style change, the theme_color is now deprecated in favor of theme. (#758)

  • page_navbar() now defaults to underline = TRUE, meaning that navigation links in the navbar now have underline styling by default (set underline = FALSE to revert to previous behavior). (#784)

  • page() now returns a <body> tag instead of tagList(). This change allows page() to treat named arguments as HTML attributes for the <body> tag, making it possible to add page-level classes or other attributes. (#809)

  • The JS/CSS assets behind bslib components (e.g., card(), value_box(), etc) are all now bundled into one htmlDependency() and included with the return value of bs_theme_dependencies() (previously they were attached at the component-level). (#810)

  • layout_column_wrap() no longer requires width and width is no longer the first argument, meaning that width must be named if used. The new default is width = "200px", which combines with fixed_width = FALSE to produce a responsive layout where each column is at least 200px wide. This means that, in most cases, layout_column_wrap() can automatically layout an unknown number of items without you having to set width. (#853)

New features

  • The default version of Bootstrap is now v5.3.1, upgraded from v5.2.2. The most notable thing that comes with the update is the ability to toggle between light/dark color modes purely client-side (i.e., no calls to Sass required). (#749, #764)

  • Added input_dark_mode(), a new input control that provides a toggle button that can be used to switch between the dark and light modes when using Bootstrap 5.3. By default, dark mode is applied automatically if the user’s operating system is also in dark mode. App authors can toggle dark mode programmatically from the server using toggle_dark_mode(), and if you provide input_dark_mode() with an id, you can read the current color mode via the corresponding input value. (#787)

  • Shiny’s Bootstrap theme preset is now used by default in bs_theme() and all related page_*() functions in bslib. This theme brings a fresh new design to all Shiny apps and dashboards created with bslib. This dashboard design anticipates heavy use of cards for organizing content, and works best with a gray background for contrast with minimalistic white cards. This treatment can be enabled in page_sidebar(), page_fillable() and other page_*() functions by adding the bslib-page-dashboard class to the page container or body tag.

    The preset can also be customized using the $bslib-dashboard-design and $bslib-enable-shadows Sass variables. Set these to false to disable the dashboard treatment and card shadows, respectively. These variables can be set via bs_theme(), e.g. bs_theme("bslib-dashboard-design" = "false"). (#897, #906)

  • value_box() has been updated with a number of new features and improvements:

    • value_box() now supports many new themes and styles, or fully customizable themes using the new value_box_theme() function. To reflect the new capabilities, we’ve replaced theme_color with a new theme argument. The previous argument will continue work as expected, but with a deprecation warning. (#758)

      In addition to the Bootstrap theme names (primary ,secondary, etc.), you can now use the main Boostrap colors (purple, blue, red, etc.). You can also choose to apply the color to the background or foreground by prepending a bg- or text- prefix to the theme or color name. Finally, we’ve also added new gradient themes allowing you to pair any two color names as bg-gradient-{from}-{to} (e.g., bg-gradient-purple-blue).

      These named color themes aren’t limited to value boxes: because they’re powered by small utility classes, you can use them anywhere within your bslib-powered UI.

    • Added showcase_bottom(), a new value_box() layout that places the showcase below the value box title and value, perfect for a full-bleed plot. (#758)

    • showcase_left_center() and showcase_top_right() no longer take two values for the width argument. Instead, they now take a single value (e.g., width = "30%") representing the width of the showcase are in the value box. Furthermore, they’ve both gained width_full_screen arguments that determine the width of the showcase area when the value box is expanded to fill the screen. (#758)

    • The showcase_layout argument of value_box() now accepts one of three character values: "left center", "top right", "bottom". (#758)

    • A new Build a Box app is now available online or via bslib. See ?value_box() for details. The app helps preview a set of value boxes while you configure and customize their appearance and provides you with code to copy and paste into your app. (#790)

  • Added new navset_underline() & navset_card_underline() functions as well as a underline argument to page_navbar() to leverage the new nav-underline styling on navigation links. (#784)


  • The bs_themer() app now supports previewing the dark mode variant of Bootstrap 5 themes. (#767)

  • Improved the style and appearance of the button to enter full screen in card()s and value_box()es to better adapt to Bootstrap’s dark mode. (#780)

  • htmltools::save_html() now works as expected when applied directly to components (e.g., card(), etc) and pages with a non-default theme. (#823, #815)

  • layout_sidebar() received a new design. The button to collapse and expand the sidebar now appears at the top (instead of the bottom) of the sidebar. On mobile devices, the sidebar now fills the layout_sidebar() area as an overlay, rather than expanding from above the main content area. Note the max_mobile_height argument of sidebar() determines the maximum height of the sidebar area on mobile, but it now only applies when open = "always". (#798)

  • layout_sidebar() now uses an <aside> element for the sidebar’s container and a <header> element for the sidebar title. The classes of each element remain the same, but the semantic meaning of the elements is now better reflected in the HTML markup. (#580)

  • In layout_column_wrap(), when width is a CSS unit – e.g. width = "400px" or width = "25%" – and fixed_width = FALSE, layout_column_wrap() will ensure that the columns are at least width wide, unless the parent container is narrower than width. (#851)

  • bs_global_theme() gains a preset argument to match the function signature of bs_theme(). (#896)

Bug fixes

  • toggle_switch() now works correctly when called from within a Shiny module. update_switch() worked as expected, but toggle_switch() didn’t apply the module’s namespace to the id of the switch to be updated. (#769)

  • Filter controls in the popovers of DT::datatable() tables now better match the current Bootstrap theme and are responsive to the dark mode setting in Bootstrap 5.3. (#267, #775).

  • A double border no longer appears when an accordion is used inside a sidebar(open="always") context. (#795)

  • layout_sidebar() no longer gives the sidebar main content area the role="main" attribute. (#580)

bslib 0.5.1

CRAN release: 2023-08-11

New features


  • Closed quarto-dev/quarto-cli#6081: bslib’s components (e.g., card(), sidebar(), etc.) now work more sensibly in Quarto docs. (#664)
  • Closed #672: sidebar() gains gap and padding arguments to control the vertical gap between items in the sidebar and the padding around the sidebar’s content. (#725)

Bug fixes

  • Closed #636: Outputs in sidebars now work as expected when an initially-closed sidebar is opened. (#624)
  • Closed #640: accordion() no longer errors when an id isn’t supplied inside a Shiny session context. (#646)
  • Closed #639: nav_panel()’s icon argument now supports generic HTML(), meaning that things like bsicons::bs_icon() and fontawesome::fa() can be used as values. (#645)
  • Light-styled buttons in bslib-provided Bootswatch themes are now consistent with their design in Bootswatch. Previously, they were inadvertently styled similarly to secondary buttons. (#687)
  • Closed #727: layout_column_wrap() now enforces equal column widths by avoiding layout issues caused by grid container overflow. (#729)

bslib 0.5.0

CRAN release: 2023-06-09

This significant release focuses on making dashboards with filling/responsive layouts easier. See the new Getting Started with Dashboards article to learn more. It also includes new components (accordion()) as well as many improvements and bug fixes for existing features and components.

bslib’s dashboarding features are still experimental at this point, but this release represents a significant step towards being our recommended way to create Shiny dashboards.

Breaking changes / improvements

  • card_body() now provides the same behavior as card_body_fill() (i.e., it is both a fillable container and fill item) by default. And, now, since card_body() can do everything card_body_fill() can do, card_body_fill() has been deprecated. The main benefit of this change is that card(full_screen = TRUE, ...) with output(s) passed to ... “just works” in an intuitive way. To revert to the previous behavior, set fillable = FALSE and fill = FALSE in calls to card_body() and set wrapper = function(x) card_body(x, fillable = FALSE, fill = FALSE) in calls to card(). (#498)
  • Closed #375: margin-top is no longer included on header tags that aren’t created via pandoc. If this negatively impacts spacing above headers, consider adding a suitable utility class (for example, change shiny::titlePanel("My title") to tagAppendAttributes(titlePanel("My title"), class = "mt-3", .selector = "h2")). (#396)
  • page_fill() (now called page_fillable()) had several breaking changes (listed below) to better accommodate filling layouts. If this breaks existing behavior, consider using shiny::fillPage(theme = bslib::bs_theme(), ...) instead of page_fill().
    • page_fill() now produces a <body> tag with display:flex (instead of display:block).
    • page_fill() no longer fills the windows height on mobile (i.e., narrow screens) by default (set fillable_mobile = TRUE to restore the old behavior).
    • page_fill() now adds padding and gap by default, set padding = 0 and gap = 0 to restore the old behavior.
  • page_navbar() (and also shiny::navbarPage() with theme = bs_theme()) had a couple breaking changes:
    • The container of each page is now display:flex (instead of display:block). If this breaks existing behavior, set page_navbar()’s fillable argument to FALSE.
    • header and footer is no longer wrapped in an additional shiny::fluidRow() container. If this breaks existing behavior, consider wrapping the header and footer value(s) with shiny::fluidRow()). (#479)
  • layout_column_wrap()’s fill argument now controls whether or not the layout container is allowed to grow/shrink to fit a fillable container (e.g., page_fillable()). It also gains a new fillable argument for controlling whether UI elements are allowed to fill their row height. This is more consistent with the meaning of fill in other functions, like card(), card_body(), layout_sidebar(), etc. (#498)
  • Defaults for the following Bootstrap 5 Sass variables were changed to null: $accordion-button-active-bg, $accordion-button-active-color, and $accordion-icon-active-color. To restore the old behavior, do bs_add_variables(theme, "accordion-button-active-bg" = "tint-color($component-active-bg, 90%)", "accordion-button-active-color" = "shade-color($primary, 10%)", "accordion-icon-active-color" = "$accordion-button-active-color", .where = "declarations"). (#475)

New features

Bug fixes

  • Closed #558: nested cards with fullscreen = TRUE now correctly and individually expand to fill the window. Tab focus behavior while in full screen mode has also been improved. (#557)
  • Closed #573: Improved styling when a dynamic result is supplied to value_box()’s title/value (e.g., value_box("Dynamic value", uiOutput("value"))). (#605)


bslib 0.4.2

CRAN release: 2022-12-16

Potentially breaking changes

  • Upgraded Bootstrap 5 (i.e., bs_theme(version = 5)) from 5.1.3 to 5.2.2. (#438, #455)

bslib 0.4.1

CRAN release: 2022-11-02

Bug Fixes

bslib 0.4.0

CRAN release: 2022-07-16

Breaking changes

  • bs_theme() now defaults to version = 5 (i.e., Bootstrap 5). If this change happens to break an existing app, consider specifying bs_theme(version = 4) to revert the change in the Bootstrap version. (#374)
  • The default coloring on some Bootswatch 4+ theme’s .navbar-default/.navbar-inverse class has changed slightly to better match their Bootswatch 3 coloring. Also, since this coloring is now based solely on $navbar-* variables, Bootswatch themes now work better in combination with custom $navbar-* values (e.g., bs_theme("navbar-bg" = ...) can be used to provide the background color, and foreground colors will automatically contrast appropriately). (#392)

New features

Bug fixes

bslib 0.3.1

CRAN release: 2021-10-06

New features

  • Upgraded Bootstrap 5 (i.e., bs_theme(version = 5)) from 5.0.2 to 5.1.0 (#365)

Bug fixes

bslib 0.3.0

CRAN release: 2021-09-02

Breaking changes

  • Closed rstudio/rmarkdown#2154: magrittr’s pipe operator (%>%) is no longer re-exported by bslib. Either library(magrittr) to make %>% available and/or use use R 4.1’s pipe operator (|>).

New features

  • Closed #82: Added support for Bootstrap 5 (via bs_theme(version = 5)). Bootstrap 4 remains the default in this release, but the next release, the default will likely change to Bootstrap 5.

Bug fixes

  • Closed #6: rmarkdown’s .tabset-fade class now works with Bootstrap 4+ since legacy use of .nav .fade is now officially supported in Bootstrap 4+. (#325)


CRAN release: 2021-05-18

Small patch release to fix failing test on Solaris.

bslib 0.2.5

CRAN release: 2021-05-12

New features and improvements

Bug fixes

bslib 0.2.4

CRAN release: 2021-01-25