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>
Note: .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:

  1. Each tab link gets data-bs-toggle="tab" and an href (or data-bs-target) pointing at the id of the panel it should reveal.
  2. Each content panel is a .tab-pane living inside a .tab-content wrapper, with a matching id.
  3. Exactly one link and one pane start out with the .active class (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>
How it works with no JS you write: Bootstrap 5's 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.

Next up: Dropdowns — another JS-free-to-you interactive component, used for menus, filters, and action lists.