Skip to content
HooksFree

useScrollSpy

Tells which section of a page or a scroll container is being read.

Pass the ids of the section headings. The hook returns the id of the last section whose top has passed the reading line, a quarter down the window or the container, and a setter to move the marker yourself. At the end of the scroll the last section wins, so a short closing section still becomes active.

See alsoFloating TOC

Usage

Overview

Plan, seats and the next invoice date of the workspace.

Billing

Payment method, billing address and the invoices of the last twelve months.

Members

Who has access, their roles and the invitations still open.

tsx
import * as React from "react"
import { useScrollSpy } from "@wunderui/react"

const sections = ["overview", "billing", "members"]

export function SettingsPanel() {
  const box = React.useRef<HTMLDivElement>(null)
  const [active] = useScrollSpy(sections, box)
  return (
    <div className="flex gap-4">
      <nav aria-label="Sections" className="flex flex-col gap-1 text-sm">
        {sections.map((id) => (
          <a key={id} href={`#${id}`} aria-current={active === id ? "true" : undefined} className={active === id ? "font-medium text-foreground" : "text-text-secondary"}>
            {id}
          </a>
        ))}
      </nav>
      <div ref={box} className="h-48 overflow-y-auto">
        {sections.map((id) => (
          <section key={id} id={id} className="min-h-40">…</section>
        ))}
      </div>
    </div>
  )
}

Import

tsx
import { useScrollSpy } from "@wunderui/react"

Part of WunderUI Free, in the npm package and the free source.

Signature

ts
useScrollSpy(ids: string[], root?: React.RefObject<HTMLElement | null> | undefined, enabled?: boolean): readonly [string | undefined, React.Dispatch<React.SetStateAction<string | undefined>>]

Parameters

ParameterTypeDefault
idsstring[]—
root?React.RefObject<HTMLElement | null> | undefined—
enabled?booleantrue

Returns

Notes

  • uses it when you leave out ; reach for the hook when you build your own navigation.
  • Pass a ref as when the sections scroll inside a container instead of the window.
  • stops listening, for example while a drawer that holds the list is closed.
  • Call the setter on click so the marker moves at once, before the smooth scroll arrives.