Skip to content

Documentation

On this page

Scrollspy

Highlight the in-page link that matches the section in view. CSS scroll-target-group and :target-current replace the ScrollSpy plugin — no JavaScript.

Sizes are minified and gzip. A plugin figure includes the Bootstrap runtime that plugin imports. Shipping every plugin is bootstrap.bundle.min.js at 23.3 KB gzip, not the sum of these rows.
What runs it JavaScript
Bootstrap 5.3 Scrollspy plugin 5.7 KB gzip
This repo scroll-target-group and :target-current 0 KB

When that API is missing, this page also loads scroll-target-group.js (0.8 KB gzip). See Polyfills.

Markup this component accepts. The full inventory is /components.json.
Avoid
  • Links in a closed [popover] menu are not scroll markers. Keep a .scroll-marker-alias copy next to the toggle.
  • The polyfill does not set aria-current. Add it yourself if you need it announced.

How it works

  • Put in-page links (href="#id") in a nav, list group, or any group of anchors. Mark that group with data-polyfill-scrollspy. That sets scroll-target-group: auto for native CSS and is what the polyfill looks for (replaces data-bs-spy="scroll" / data-bs-target). An optional selector value points at the scroller; otherwise the overflow ancestor of the targets is used.
  • Give each section an id that matches a link. The scrollable box can be the page or an element with a height and overflow: auto.
  • :target-current styles the marker whose target is in view. It supplements .active with the same look, so either class or the CSS scroll marker can highlight a link.
  • A parent .nav-link or dropdown toggle stays highlighted with :has(:target-current) when a nested marker is current.
  • scroll-behavior: smooth on the scroller replaces data-bs-smooth-scroll.

Navbar

Scroll the area below the navbar and watch the current link change. Open the dropdown to see its items highlighted too.

First heading

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

Second heading

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

Third heading

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

Fourth heading

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

Fifth heading

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

HTML
<nav id="navbar-example2" class="navbar bg-body-tertiary px-3 mb-3" data-polyfill-scrollspy>
  <a class="navbar-brand" href="#">Navbar</a>
  <ul class="nav nav-pills">
    <li class="nav-item">
      <a class="nav-link" href="#scrollspyHeading1">First</a>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="#scrollspyHeading2">Second</a>
    </li>
    <li class="nav-item dropdown">
      <button class="nav-link dropdown-toggle" type="button" command="toggle-popover" commandfor="scrollspyDropdown">Dropdown</button>
      <ul class="dropdown-menu" popover="auto" id="scrollspyDropdown">
        <li><a class="dropdown-item" href="#scrollspyHeading3">Third</a></li>
        <li><a class="dropdown-item" href="#scrollspyHeading4">Fourth</a></li>
        <li><hr class="dropdown-divider"></li>
        <li><a class="dropdown-item" href="#scrollspyHeading5">Fifth</a></li>
      </ul>
      <span class="scroll-marker-alias" aria-hidden="true">
        <a href="#scrollspyHeading3" tabindex="-1">Third</a>
        <a href="#scrollspyHeading4" tabindex="-1">Fourth</a>
        <a href="#scrollspyHeading5" tabindex="-1">Fifth</a>
      </span>
    </li>
  </ul>
</nav>
<div class="scrollspy-example bg-body-tertiary p-3 rounded-2" tabindex="0">
  <h4 id="scrollspyHeading1">First heading</h4>
  <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
  <h4 id="scrollspyHeading2">Second heading</h4>
  <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
  <h4 id="scrollspyHeading3">Third heading</h4>
  <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
  <h4 id="scrollspyHeading4">Fourth heading</h4>
  <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
  <h4 id="scrollspyHeading5">Fifth heading</h4>
  <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
</div>

Nested nav

Scrollspy also works with nested .navs. If a nested link is current, its parent nav-link is highlighted as well.

Item 1

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

Keep in mind that the browser picks one current scroll marker. Multiple visible targets at the same time may still look busy.

Item 1-1

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

Keep in mind that the browser picks one current scroll marker. Multiple visible targets at the same time may still look busy.

Item 1-2

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

Keep in mind that the browser picks one current scroll marker. Multiple visible targets at the same time may still look busy.

Item 2

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

Keep in mind that the browser picks one current scroll marker. Multiple visible targets at the same time may still look busy.

Item 3

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

Keep in mind that the browser picks one current scroll marker. Multiple visible targets at the same time may still look busy.

Item 3-1

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

Keep in mind that the browser picks one current scroll marker. Multiple visible targets at the same time may still look busy.

Item 3-2

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

Keep in mind that the browser picks one current scroll marker. Multiple visible targets at the same time may still look busy.

HTML
<div class="row">
  <div class="col-4">
    <nav id="navbar-example3" class="h-100 flex-column align-items-stretch pe-4 border-end">
      <nav class="nav nav-pills flex-column" data-polyfill-scrollspy>
        <a class="nav-link" href="#item-1">Item 1</a>
        <nav class="nav nav-pills flex-column">
          <a class="nav-link ms-3 my-1" href="#item-1-1">Item 1-1</a>
          <a class="nav-link ms-3 my-1" href="#item-1-2">Item 1-2</a>
        </nav>
        <a class="nav-link" href="#item-2">Item 2</a>
        <a class="nav-link" href="#item-3">Item 3</a>
        <nav class="nav nav-pills flex-column">
          <a class="nav-link ms-3 my-1" href="#item-3-1">Item 3-1</a>
          <a class="nav-link ms-3 my-1" href="#item-3-2">Item 3-2</a>
        </nav>
      </nav>
    </nav>
  </div>
  <div class="col-8">
    <div class="scrollspy-example-2" tabindex="0">
      <div id="item-1">
        <h4>Item 1</h4>
        <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
        <p>Keep in mind that the browser picks one current scroll marker. Multiple visible targets at the same time may still look busy.</p>
      </div>
      <div id="item-1-1">
        <h5>Item 1-1</h5>
        <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
        <p>Keep in mind that the browser picks one current scroll marker. Multiple visible targets at the same time may still look busy.</p>
      </div>
      <div id="item-1-2">
        <h5>Item 1-2</h5>
        <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
        <p>Keep in mind that the browser picks one current scroll marker. Multiple visible targets at the same time may still look busy.</p>
      </div>
      <div id="item-2">
        <h4>Item 2</h4>
        <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
        <p>Keep in mind that the browser picks one current scroll marker. Multiple visible targets at the same time may still look busy.</p>
      </div>
      <div id="item-3">
        <h4>Item 3</h4>
        <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
        <p>Keep in mind that the browser picks one current scroll marker. Multiple visible targets at the same time may still look busy.</p>
      </div>
      <div id="item-3-1">
        <h5>Item 3-1</h5>
        <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
        <p>Keep in mind that the browser picks one current scroll marker. Multiple visible targets at the same time may still look busy.</p>
      </div>
      <div id="item-3-2">
        <h5>Item 3-2</h5>
        <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
        <p>Keep in mind that the browser picks one current scroll marker. Multiple visible targets at the same time may still look busy.</p>
      </div>
    </div>
  </div>
</div>

List group

Scrollspy also works with .list-groups. Scroll the area next to the list group and watch the current item change.

Item 1

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

Item 2

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

Item 3

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

Item 4

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

HTML
<div class="row">
  <div class="col-4">
    <div id="list-example" class="list-group" data-polyfill-scrollspy>
      <a class="list-group-item list-group-item-action" href="#list-item-1">Item 1</a>
      <a class="list-group-item list-group-item-action" href="#list-item-2">Item 2</a>
      <a class="list-group-item list-group-item-action" href="#list-item-3">Item 3</a>
      <a class="list-group-item list-group-item-action" href="#list-item-4">Item 4</a>
    </div>
  </div>
  <div class="col-8">
    <div class="scrollspy-example" tabindex="0">
      <h4 id="list-item-1">Item 1</h4>
      <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
      <h4 id="list-item-2">Item 2</h4>
      <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
      <h4 id="list-item-3">Item 3</h4>
      <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
      <h4 id="list-item-4">Item 4</h4>
      <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
    </div>
  </div>
</div>

Simple anchors

Scrollspy is not limited to nav components and list groups. Any fragment link in a data-polyfill-scrollspy group works.

Item 1

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

Item 2

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

Item 3

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

Item 4

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

Item 5

This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.

HTML
<div class="row">
  <div class="col-4">
    <div id="simple-list-example" class="d-flex flex-column gap-2 simple-list-example-scrollspy text-center" data-polyfill-scrollspy>
      <a class="p-1 rounded" href="#simple-list-item-1">Item 1</a>
      <a class="p-1 rounded" href="#simple-list-item-2">Item 2</a>
      <a class="p-1 rounded" href="#simple-list-item-3">Item 3</a>
      <a class="p-1 rounded" href="#simple-list-item-4">Item 4</a>
      <a class="p-1 rounded" href="#simple-list-item-5">Item 5</a>
    </div>
  </div>
  <div class="col-8">
    <div class="scrollspy-example" tabindex="0">
      <h4 id="simple-list-item-1">Item 1</h4>
      <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
      <h4 id="simple-list-item-2">Item 2</h4>
      <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
      <h4 id="simple-list-item-3">Item 3</h4>
      <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
      <h4 id="simple-list-item-4">Item 4</h4>
      <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
      <h4 id="simple-list-item-5">Item 5</h4>
      <p>This is some placeholder content for the scrollspy page. As the area scrolls, the matching navigation link is highlighted. The same paragraph is repeated so the scroller is long enough to move the highlight from heading to heading.</p>
    </div>
  </div>
</div>

Accessibility

:target-current is visual only. Assistive technology still needs aria-current="true" on the in-view link, which requires script. This demo does not set it. The links remain ordinary in-page links.

Browser support

Three columns, not one baseline. The same row is on the overview.
No script Polyfill No fallback
scroll-target-group: Chrome 140+. IntersectionObserver when the CSS is missing. It does not set aria-current. Marked experimental on the component page. The polyfill covers browsers without the CSS.