1. Plugins
  2. Scrollspy

Plugins

Tailwind CSS Scrollspy

Automatically update navigation or list group components based on scroll position to indicate which link is currently active in the viewport.

Scrollspy

Installation

To get started with Scrollspy, install Overlay plugin via npm, else you can skip this step if you are already using Preline UI as a package.

                      
                        npm i @preline/scrollspy
                      
                    

Example

Scroll the area below the navbar and watch the active class change. The dropdown items will be highlighted as well.

First

This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It's repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.

Second

This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It's repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.

Third

This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It's repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.

Fourth

This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It's repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.

Fifth

This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It's repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.

Basic usage

Prefer to create your own style? Here is a completely unstylized example.

                      
                        <header class="sticky top-0 inset-x-0 flex flex-wrap sm:justify-start sm:flex-nowrap z-40 w-full bg-white text-sm py-4 dark:bg-neutral-800">
                          <nav class="max-w-[85rem] w-full mx-auto sm:flex sm:items-center sm:justify-between">
                            <div class="flex items-center justify-between">
                              <a class="flex-none text-xl font-semibold text-gray-800 dark:text-neutral-200" href="#">Brand</a>
                              <div class="sm:hidden">
                                <button type="button" class="hs-collapse-toggle p-2 inline-flex justify-center items-center gap-2 rounded-lg border font-medium bg-white text-gray-700 shadow-sm align-middle hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-white focus:ring-blue-600 transition-all text-sm" id="hs-scrollspy" aria-expanded="false" aria-controls="hs-scrollspy-heading" data-hs-collapse="#hs-scrollspy-heading">
                                  <svg class="hs-collapse-open:hidden size-4" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
                                    <path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"/>
                                  </svg>
                                  <svg class="hs-collapse-open:block hidden size-4" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
                                    <path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/>
                                  </svg>
                                </button>
                              </div>
                            </div>
                            <div id="hs-scrollspy-heading" class="hidden overflow-hidden transition-all duration-300 basis-full grow sm:block" aria-labelledby="hs-scrollspy">
                              <div data-hs-scrollspy="#scrollspy-1" data-hs-scrollspy-scrollable-parent="#scrollspy-scrollable-parent-1" class="mt-5 [--scrollspy-offset:220] md:[--scrollspy-offset:70] flex flex-col gap-5 sm:flex-row sm:items-center sm:justify-end sm:mt-0 sm:ps-5">
                                <a class="hs-scrollspy-active:text-blue-600 text-sm text-gray-700 leading-6 hover:text-gray-500 focus:outline-none focus:text-gray-500 dark:text-neutral-300 dark:hover:text-white dark:focus:text-white dark:hs-scrollspy-active:text-blue-500 active" href="#first">First</a>
                                <a class="hs-scrollspy-active:text-blue-600 text-sm text-gray-700 leading-6 hover:text-gray-500 focus:outline-none focus:text-gray-500 dark:text-neutral-300 dark:hover:text-white dark:focus:text-white dark:hs-scrollspy-active:text-blue-500" href="#second">Second</a>

                                <div data-hs-scrollspy-group class="hs-dropdown [--strategy:static] sm:[--strategy:fixed] [--adaptive:none] sm:[--placement:bottom-right] ">
                                  <button id="hs-dropdown-scrollspy" type="button" id="hs-mega-menu-basic-dr" class="group hs-scrollspy-active:text-blue-600 mb-3 sm:mb-0 inline-flex justify-center items-center gap-1 text-sm text-gray-700 leading-6 hover:text-gray-500 focus:outline-none focus:text-gray-500 dark:text-neutral-300 dark:hover:text-white dark:focus:text-white dark:hs-scrollspy-active:text-blue-500" aria-haspopup="menu" aria-expanded="false" aria-label="Dropdown">
                                    Dropdown
                                    <svg class="ms-2 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
                                  </button>

                                  <div class="hs-dropdown-menu transition-[opacity,margin] duration-[0.1ms] sm:duration-[150ms] hs-dropdown-open:opacity-100 opacity-0 sm:w-48 z-10 bg-white sm:shadow-md rounded-lg sm:p-2 before:absolute top-full sm:border before:-top-5 before:start-0 before:w-full before:h-5 hidden dark:bg-neutral-800 dark:border-neutral-700" role="menu" aria-orientation="vertical" aria-labelledby="hs-dropdown-scrollspy">
                                    <a class="hs-scrollspy-active:text-blue-600 flex items-center gap-x-3.5 py-2 px-3 rounded-lg text-sm text-gray-700 leading-6 hover:text-gray-500 focus:outline-none focus:text-gray-500 dark:text-white dark:hover:text-neutral-300 dark:focus:text-neutral-300" href="#third">
                                      Third
                                    </a>
                                    <a class="hs-scrollspy-active:text-blue-600 flex items-center gap-x-3.5 py-2 px-3 rounded-lg text-sm text-gray-700 leading-6 hover:text-gray-500 focus:outline-none focus:text-gray-500 dark:text-white dark:hover:text-neutral-300 dark:focus:text-neutral-300" href="#fourth">
                                      Fourth
                                    </a>
                                    <a class="hs-scrollspy-active:text-blue-600 flex items-center gap-x-3.5 py-2 px-3 rounded-lg text-sm text-gray-700 leading-6 hover:text-gray-500 focus:outline-none focus:text-gray-500 dark:text-white dark:hover:text-neutral-300 dark:focus:text-neutral-300" href="#fifth">
                                      Fifth
                                    </a>
                                  </div>
                                </div>
                              </div>
                            </div>
                          </nav>
                        </header>

                        <div id="scrollspy-1" class="mt-3 space-y-4">
                          <div id="first">
                            <h3 class="text-lg font-semibold text-gray-800 dark:text-neutral-200">First</h3>
                            <p class="mt-1 text-sm leading-6 text-gray-600 dark:text-neutral-500">This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It's repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.</p>
                          </div>

                          <div id="second">
                            <h3 class="text-lg font-semibold text-gray-800 dark:text-neutral-200">Second</h3>
                            <p class="mt-1 text-sm leading-6 text-gray-600 dark:text-neutral-500">This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It's repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.</p>
                          </div>

                          <div id="third">
                            <h3 class="text-lg font-semibold text-gray-800 dark:text-neutral-200">Third</h3>
                            <p class="mt-1 text-sm leading-6 text-gray-600 dark:text-neutral-500">This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It's repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.</p>
                          </div>

                          <div id="fourth">
                            <h3 class="text-lg font-semibold text-gray-800 dark:text-neutral-200">Fourth</h3>
                            <p class="mt-1 text-sm leading-6 text-gray-600 dark:text-neutral-500">This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It's repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.</p>
                          </div>

                          <div id="fifth">
                            <h3 class="text-lg font-semibold text-gray-800 dark:text-neutral-200">Fifth</h3>
                            <p class="mt-1 text-sm leading-6 text-gray-600 dark:text-neutral-500">This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It's repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.</p>
                          </div>
                        </div>
                      
                    

Data Options

Name Description Options Default value
data-hs-scrollspy A container containing sections. This must be a valid selector. Should be added to the nav that contains Scrollspy links. string
data-hs-scrollspy-scrollable-parent Specifies the element to be scrolled. This must be a valid selector. Should be added to the nav that contains Scrollspy links. string window

Class Options

Name Description Options Default value
[--scrollspy-offset:*] Adds offset when scrolling to the section and to determine if the section is active. Should be added to the nav that contains Scrollspy links. number 0

Tailwind Modifiers

Name Description
hs-scrollspy-active:* A modifier that allows you to set Tailwind classes when the section is active for link

Methods

The HSScrollspy object is contained within the global window object

Method Description
Public methods
destroy() Destroys the instance, removes generated markup (if any), removes added classes and attributes.
Static methods
HSScrollspy.getInstance(target, isInstance) Returns the element associated to the target.
  • target should be a Node or string (valid selector)
  • isInstance boolean. Returns the instance instead of Node if true

Destroy instance.

                      
                        const { element } = HSScrollspy.getInstance('#scrollspy', true);
                        const destroyBtn = document.querySelector('#destroy-btn');
                        
                        destroyBtn.addEventListener('click', () => {
                          element.destroy();
                        });
                      
                    

Events

Method Description Returning value
on:beforeScroll Called before scrolling begins. Could be Promise. Instance

An example of executing a function after collapsing the accordion.

                      
                        const el = HSScrollspy.getInstance('[data-hs-scrollspy="#scrollspy"]', true);
                        const collapse = HSCollapse.getInstance('[data-hs-collapse="#navbar-collapse-basic"]', true);
                
                        el.element.on('beforeScroll', (instance) => {
                          return new Promise((res) => {
                            if (collapse.element.el.classList.contains('open')) {
                              collapse.element.hide();
                              HSStaticMethods.afterTransition(collapse.element.content, () => res(true));
                            } else {
                              res(true);
                            }
                          });
                        });
                      
                    

Demo examples

Looking for prebuilt UI components based on the Tailwind CSS? Preline UI packs hundreds of component examples for all your website needs.

Plugins UI Mockups
Check out Preline UI Components