Skip to contents

This article covers the Bootstrap component library: cards, alerts, badges, buttons, accordions, carousels, collapses, list groups, progress bars, spinners and placeholders. Navigation components (nav/tabs, navbar, breadcrumb, pagination, dropdown) have their own article; overlays (modal, offcanvas, toast, tooltips) are in Overlays.

Some of these components are interactive — they report state as input$id and are driven from the server with an update_bs_*() helper. Those follow the same pattern throughout bootstrict; see Overlays and server-driven interactivity for the full story.

Buttons

bs_button() renders a Bootstrap button. It becomes a Shiny action button only when given an id — then input$id is the click count, exactly like shiny::actionButton(). Without an id it is an inert, styled button.

bs_button(
  id = NULL,
  label = NULL,
  ...,
  color = "primary",   # a theme colour or "link"
  outline = FALSE,     # .btn-outline-*
  size = NULL,         # "sm" or "lg"
  disabled = FALSE,
  href = NULL,         # set to render an <a> styled as a button
  type = "button",     # "button"/"submit"/"reset"
  class = NULL
)
bs_button("go", "Go", color = "primary")
bs_button(label = "Cancel", color = "secondary", outline = TRUE)

Group buttons with bs_button_group() (add vertical = TRUE for a column), and group groups with bs_button_toolbar():

bs_button_group(
  bs_button(label = "Left"),
  bs_button(label = "Right")
)

The standalone Bootstrap close button (used inside dismissible components) is bs_close_button(). On a dark background use white = TRUE.

Download buttons

bs_download_button() and bs_download_link() wrap the Shiny originals. Shiny hardcodes the Bootstrap 3 class .btn-default, which has no Bootstrap 5 equivalent and leaves the button unstyled; these take color / outline / size like bs_button() instead. The server side stays a plain shiny::downloadHandler().

ui <- bs_page(
  bs_container(bs_download_button("save", "Download CSV", color = "success"))
)

server <- function(input, output, session) {
  output$save <- downloadHandler(
    filename = "data.csv",
    content = function(file) write.csv(mtcars, file)
  )
}

icon defaults to NULL rather than Shiny’s Font Awesome icon, which is outside Bootstrap 5.

Cards

Cards are built from composable pieces. bs_card() is the container; the rest (bs_card_header(), bs_card_body(), bs_card_title(), bs_card_subtitle(), bs_card_text(), bs_card_footer(), bs_card_link()) map one-to-one to the Bootstrap card sub-elements.

bs_card(
  bs_card_header("Featured"),
  bs_card_body(
    bs_card_title("Card title"),
    bs_card_subtitle("A subtitle"),
    bs_card_text("Some quick example text to build on the card title."),
    bs_card_link("Read more", href = "#")
  ),
  bs_card_footer("Updated 3 mins ago")
)

bs_card() accepts color= (a background theme colour, .text-bg-*) and border= (a border colour). For images, bs_card_img() places a picture at the "top", "bottom" or as an "overlay"; pair the overlay with bs_card_img_overlay() to float text on top. bs_card_group() snaps several cards into an equal-width attached grid.

Alerts

bs_alert() is a coloured message box. dismissible = TRUE adds a close button and fade-out.

bs_alert("Well done! You read this important message.", color = "success")
bs_alert("Heads up — check your settings.", color = "warning", dismissible = TRUE)

Inside an alert, bs_alert_heading() gives a matched heading and bs_alert_link() a colour-matched link:

bs_alert(
  color = "danger",
  bs_alert_heading("Something went wrong"),
  "Read the ", bs_alert_link("full log", href = "#"), " for details."
)

Give it an id and it becomes interactive: input$id is TRUE while the alert is on the page and FALSE once dismissed, and close_bs_alert() dismisses it from the server. Bootstrap removes the element, so an alert cannot be brought back — render it from a renderUI() if it has to come and go.

ui <- bs_page(
  bs_container(
    bs_alert("Saved.", id = "saved", color = "success", dismissible = TRUE),
    bs_button("dismiss", "Dismiss")
  )
)

server <- function(input, output, session) {
  observeEvent(input$dismiss, close_bs_alert("saved"))
  observe(message("alert still shown: ", input$saved))
}

Badges

Small count/label pills:

bs_badge("New", color = "success")
bs_badge("42", color = "danger", pill = TRUE)

Accordion (interactive)

A vertically collapsing set of panels. The value(s) of the open panel(s) are reported as input$id, and you open/close them from the server with update_bs_accordion().

bs_accordion(
  "acc",
  bs_accordion_panel("First", "Panel one body", value = "one"),
  bs_accordion_panel("Second", "Panel two body", value = "two"),
  open = "one"
)
  • open accepts panel value(s), or TRUE (open all — only sensible with multiple = TRUE), or FALSE/NULL (none).
  • multiple = TRUE lets panels stay open independently (“always open”).
  • flush = TRUE renders edge-to-edge.
server <- function(input, output, session) {
  observe(print(input$acc))                          # value(s) of open panel(s)
  observeEvent(input$go, update_bs_accordion("acc", open = "two"))
}

A slideshow of bs_carousel_item()s. The 0-based index of the active slide is reported as input$id; drive it with update_bs_carousel().

bs_carousel(
  "demo",
  bs_carousel_item(bs_img("1.jpg"), active = TRUE, caption = "First slide"),
  bs_carousel_item(bs_img("2.jpg"))
)
  • indicators / controls toggle the clickable dots and prev/next arrows.
  • fade = TRUE crossfades; autoplay = TRUE (default) auto-cycles; interval sets the cycle time in ms.
  • Exactly one item is active (the first is defaulted if you set none).
update_bs_carousel("demo", to = 2)       # jump to a slide (0-based)
update_bs_carousel("demo", slide = "next")

Collapse (interactive)

A container that shows/hides content. Its state is reported as input$id (TRUE when visible). Toggle it from the UI with bs_collapse_trigger() (no server round trip) or from the server with update_bs_collapse().

tagList(
  bs_collapse_trigger("more", "Toggle details"),
  bs_collapse("more", "Hidden content revealed on toggle.")
)
update_bs_collapse("more", "show")   # "toggle" (default), "show" or "hide"

List group (interactive)

A flexible list that becomes server-selectable when you give it an id: the active item’s value is reported as input$id, and update_bs_list_group() activates an item by value.

bs_list_group(
  "picker",
  bs_list_group_item("An item", value = "a", action = TRUE),
  bs_list_group_item("A second item", value = "b", action = TRUE)
)

Items can be plain (<li>), actionable (action = TRUE → a <button>), or links (href= → an <a>). flush = TRUE, numbered = TRUE and horizontal = TRUE change the layout; color= gives an item a contextual colour.

update_bs_list_group("picker", selected = "b")

Progress (interactive)

bs_progress() is the track; bs_progress_bar() is a filled bar. Pass two or more bars to bs_progress() and it renders a Bootstrap 5.3 .progress-stacked group. Give a bar an id to update it from the server with update_bs_progress().

bs_progress(bs_progress_bar(value = 25, id = "load", color = "success"))

# stacked (5.3):
bs_progress(
  height = "1.5rem",
  bs_progress_bar(value = 15, color = "success", label = "15%"),
  bs_progress_bar(value = 30, color = "danger",  label = "30%")
)
update_bs_progress("load", value = 80, label = "80%")

A bar supports striped, animated (implies striped), min/max bounds, and an aria_label for accessibility.

Spinners and placeholders

bs_spinner() is a loading indicator — a spinning border or a pulsing dot:

bs_spinner(type = "border", color = "primary")
bs_spinner(type = "grow", color = "secondary", size = "sm")

Placeholders are “skeleton” blocks shown while content loads. bs_placeholder() is one block (width is a 1–12 column count); wrap the whole skeleton in bs_placeholder_glow() or bs_placeholder_wave() to animate it:

bs_placeholder_glow(
  bs_placeholder(width = 7),
  bs_placeholder(width = 4),
  bs_placeholder(width = 6)
)