The navigation family: static navs, interactive tabsets, the navbar, breadcrumbs, pagination and dropdowns.
Nav vs. tabset
There are two related things here, and it is worth being clear about the difference:
-
bs_nav()is a static navigation bar — a styled row of links. It reports no state; you decide what each link does. -
bs_tabset()is an interactive tabbed-panel set. It reports the active panel asinput$idand is driven from the server withupdate_bs_tabset(). This is thebootstrictequivalent oftabsetPanel()/tabPanel().
Static nav
bs_nav(
bs_nav_item(bs_nav_link("Active", active = TRUE)),
bs_nav_item(bs_nav_link("Link", href = "#")),
bs_nav_item(bs_nav_link("Disabled", disabled = TRUE)),
type = "tabs" # NULL (plain), "tabs", "pills" or "underline" (5.3)
)fill, justified and vertical
control how the links spread and stack.
Interactive tabset
Build a tabset from bs_tab_panel()s. An id
is required, and each panel’s value (defaulting to its
title) is what gets reported.
bs_tabset(
"tabs",
type = "tabs", # "tabs" (default), "pills" or "underline"
selected = "profile",
bs_tab_panel("Home", "Home content", value = "home"),
bs_tab_panel("Profile", "Profile content", value = "profile")
)Read the active tab as input$tabs, and switch it from
the server:
server <- function(input, output, session) {
observe(print(input$tabs)) # active panel value
observeEvent(input$go, update_bs_tabset("tabs", selected = "home"))
}The Bootstrap 5.3 .nav-underline style is available via
type = "underline" — used, for example, in the bundled
Quake Watch showcase.
Navbar
bs_navbar() is a responsive header that collapses behind
a toggler below the expand breakpoint.
bs_navbar(
...,
brand = NULL, # usually a bs_navbar_brand()
id = NULL, # auto-generated if omitted
expand = "lg", # "sm"/"md"/"lg"/"xl"/"xxl", or TRUE (always) / FALSE (never)
bg = NULL, # a theme colour, or "body"/"body-secondary"/"white"/…
theme = NULL, # "light" or "dark" (data-bs-theme)
placement = NULL, # "fixed-top"/"fixed-bottom"/"sticky-top"/"sticky-bottom"
fluid = TRUE,
class = NULL
)A typical navbar pairs a brand, a nav list and some text. Note the
5.3 idiom: for a dark navbar, set theme = "dark" (not the
deprecated .navbar-dark).
bs_navbar(
brand = bs_navbar_brand("Quake Watch", href = "/"),
bs_navbar_nav(
bs_nav_item(bs_nav_link("Monitor", active = TRUE)),
bs_nav_item(bs_nav_link("Docs", href = "#")),
bs_nav_dropdown(
"More",
bs_dropdown_item("Settings", id = "settings"),
bs_dropdown_divider(),
bs_dropdown_item("Sign out", id = "signout")
)
),
bs_navbar_text("Fiji region · 1964 →"),
bg = "primary",
theme = "dark"
)bs_navbar_nav() can scroll a long link list:
scroll = TRUE with an optional
scroll_height = "75vh".
A navbar that switches pages
Bootstrap has no “navbar page” component: a navbar is a header, and
switching content is the application’s job. There is no
navbarPage() equivalent here on purpose, but the pattern is
short. Give the navbar’s links values with
bs_nav_dropdown() or drive a hidden tabset, and swap the
content yourself.
ui <- bs_page(
bs_navbar(
brand = bs_navbar_brand("My app"),
bs_navbar_nav(
bs_nav_item(bs_nav_link("Monitor", active = TRUE, value = "monitor")),
bs_nav_item(bs_nav_link("Docs", value = "docs")),
id = "nav"
)
),
bs_container(uiOutput("page"))
)
server <- function(input, output, session) {
output$page <- renderUI({
switch(input$nav %||% "monitor",
monitor = bs_card(bs_card_body("The monitor")),
docs = bs_card(bs_card_body("The docs"))
)
})
}bs_navbar_nav(id =) reports its active link the same way
bs_nav(id =) does, and update_bs_nav() sets
it. Without an id the links stay inert, which is what a
navbar of plain anchors should be.
Breadcrumbs
bs_breadcrumb() composes
bs_breadcrumb_item()s; the last item is usually
active = TRUE (the current page, rendered without a
link).
bs_breadcrumb(
bs_breadcrumb_item("Home", href = "#"),
bs_breadcrumb_item("Library", href = "#"),
bs_breadcrumb_item("Data", active = TRUE)
)divider= sets the separator character via the
--bs-breadcrumb-divider CSS variable,
e.g. divider = ">".
Pagination
Build pagination by hand from bs_page_item()s:
bs_pagination(
bs_page_item("Previous", href = "#"),
bs_page_item("1", href = "#", active = TRUE),
bs_page_item("2", href = "#"),
bs_page_item("Next", href = "#"),
size = "sm", # "sm" or "lg"
align = "center" # "start"/"center"/"end"
)…or let bs_pagination_numbered() lay out Previous /
1..n / Next for you:
bs_pagination_numbered(5, current = 2, href_template = "?page=%d")href_template is a sprintf()-style pattern;
without it every link is "#".
Dropdowns
bs_dropdown() builds a toggleable menu. To make a menu
item reactive, give it an id — it then behaves as a Shiny
action button, reporting its click count as input$id. Give
the dropdown an id and its open state is reported
too, and show_bs_dropdown() /
hide_bs_dropdown() / toggle_bs_dropdown()
drive it from the server.
bs_dropdown(
"Menu",
bs_dropdown_header("Actions"),
bs_dropdown_item("Edit", id = "edit"),
bs_dropdown_item("Duplicate", id = "dup"),
bs_dropdown_divider(),
bs_dropdown_text("Signed in as Colin"),
color = "secondary", # a theme colour or "link"
outline = TRUE
)
server <- function(input, output, session) {
observeEvent(input$edit, message("Edit clicked"))
}Other options: size
("sm"/"lg"), split = TRUE for a
split button, direction
("down"/"up"/"end"/"start"),
align (right-align the menu, or a responsive
list(lg = "end")), and dark = TRUE for a dark
menu (the 5.3 data-bs-theme idiom).
Inside a nav or a navbar, use bs_nav_dropdown() instead:
Bootstrap’s menu there is an
<li class="nav-item dropdown"> whose toggle is a
.nav-link. bs_dropdown() would emit a
<div> as a direct child of the
<ul class="navbar-nav"> — invalid HTML that renders
as a grey button. The menu items are the same.
bs_nav_dropdown(
"More",
bs_dropdown_item("Settings", id = "settings"),
bs_dropdown_divider(),
bs_dropdown_item("Sign out", id = "signout"),
align = "end"
)