Nav & Tabs
Underneath the navbar you just learned sits a more general building block: the
.nav component — a flexible, unstyled list of links that other components (navbars,
tabs, pills) build on top of. In this lesson we style it as tabs and pills, then wire the tabs up so
clicking one actually swaps visible content — no jQuery, just data-bs-toggle="tab" and
Bootstrap 5's own JS bundle. We'll finish with a TrailMap trail detail page that uses tabs for
Overview / Reviews / Photos.
1. The Base .nav Component
On its own, .nav just turns a list into a horizontal flex row of links — no borders,
no active-state styling. Each entry is a .nav-item wrapping a .nav-link.
<ul class="nav">
<li class="nav-item">
<a class="nav-link active" href="#">Overview</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Reviews</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Photos</a>
</li>
</ul>
2. Tab-Styled: .nav-tabs
Add .nav-tabs to the list and Bootstrap draws a bottom border under the whole row, with
the .active link rendered as a raised "tab" that visually connects to the content below
it.
<ul class="nav nav-tabs">
<li class="nav-item">
<a class="nav-link active" href="#">Overview</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Reviews</a>
</li>
</ul>
3. Pill-Styled: .nav-pills
Swap .nav-tabs for .nav-pills and you get rounded "pill" buttons instead of
a bordered tab strip — same underlying HTML, a completely different look. Add
.flex-column to stack pills vertically for a sidebar-style nav.
<ul class="nav nav-pills">
<li class="nav-item">
<a class="nav-link active" href="#">Overview</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Reviews</a>
</li>
</ul>
.nav-tabs and .nav-pills are purely visual — neither one makes the tabs functional by itself. That takes the data-bs-toggle attribute covered next.4. Making Tabs Functional With data-bs-toggle="tab"
To turn a styled nav into working tabs, three pieces have to line up:
- Each tab link gets
data-bs-toggle="tab"and anhref(ordata-bs-target) pointing at the id of the panel it should reveal. - Each content panel is a
.tab-paneliving inside a.tab-contentwrapper, with a matchingid. - Exactly one link and one pane start out with the
.activeclass (and the pane also needs.show) so something is visible before any click happens.
<!-- The tab strip -->
<ul class="nav nav-tabs" id="trailTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="overview-tab"
data-bs-toggle="tab" data-bs-target="#overview-pane"
type="button" role="tab" aria-selected="true">Overview</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="reviews-tab"
data-bs-toggle="tab" data-bs-target="#reviews-pane"
type="button" role="tab" aria-selected="false">Reviews</button>
</li>
</ul>
<!-- The matching content panels -->
<div class="tab-content" id="trailTabContent">
<div class="tab-pane fade show active" id="overview-pane" role="tabpanel">
Overview content goes here.
</div>
<div class="tab-pane fade" id="reviews-pane" role="tabpanel">
Reviews content goes here.
</div>
</div>
bootstrap.bundle.min.js listens for clicks on any element with data-bs-toggle="tab", removes .active/.show from the currently visible pane, and adds them to the target pane — plain vanilla JavaScript under the hood, nothing to wire up yourself.5. Worked Example — TrailMap Trail Detail Page
A trail detail page for "Eagle Peak Trail" with three tabs — Overview, Reviews, and Photos — sharing one content area below the tab strip.
<h3>Eagle Peak Trail</h3>
<ul class="nav nav-tabs" id="eagleTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" data-bs-toggle="tab"
data-bs-target="#eagle-overview" type="button">Overview</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" data-bs-toggle="tab"
data-bs-target="#eagle-reviews" type="button">Reviews (48)</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" data-bs-toggle="tab"
data-bs-target="#eagle-photos" type="button">Photos</button>
</li>
</ul>
<div class="tab-content border border-top-0 p-3">
<div class="tab-pane fade show active" id="eagle-overview">
<p>7.2 mi round trip · 1,450 ft elevation gain · Difficulty: Hard</p>
</div>
<div class="tab-pane fade" id="eagle-reviews">
<p>★★★★☆ "Steep but the summit view is worth it." – hikerjane</p>
</div>
<div class="tab-pane fade" id="eagle-photos">
<p>12 trail photos submitted by hikers.</p>
</div>
</div>
Clicking "Reviews (48)" swaps the visible pane instantly — Bootstrap toggles the .active
classes on both the buttons and the panes for you, and it updates aria-selected so
screen readers announce the change too.