Navigation

Anchor nav

A table of contents that knows where you are — the "On this page" column.

Client componentSource

Import

import { AnchorNav } from '@the_viveksingh/vivek-ui'

On this page

Real hash links that work before JavaScript, then an IntersectionObserver marks the section in view with aria-current="location". offset accounts for a fixed header in both the spy and the jump. Clicking scrolls smoothly (instantly under prefers-reduced-motion), replaces the hash without a history entry, and moves focus to the section.

Overview

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

Installation

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

With npm

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

With pnpm

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

Usage

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

Theming

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

<AnchorNav
  title="On this page"
  offset={64}
  items={[
    { id: 'overview', label: 'Overview' },
    {
      id: 'install',
      label: 'Installation',
      children: [
        { id: 'npm', label: 'With npm' },
        { id: 'pnpm', label: 'With pnpm' },
      ],
    },
    { id: 'usage', label: 'Usage' },
  ]}
  style={{ position: 'sticky', top: '5rem' }}
/>

Horizontal, under a page header

orientation="horizontal" turns the rail into one scrolling row with an underline; nested items are hidden there. onActiveChange reports the current id; pass activeId to drive it yourself.

Overview

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

Installation

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

With npm

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

With pnpm

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

Usage

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

Theming

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

Scroll this column. The list on the left follows the heading nearest the top and marks it aria-current="location". Every entry is a real hash link, so it works before JavaScript and copies as a link.

<AnchorNav
  orientation="horizontal"
  label="Sections"
  items={[
    { id: 'summary', label: 'Summary' },
    { id: 'activity', label: 'Activity' },
    { id: 'billing', label: 'Billing' },
    { id: 'members', label: 'Members' },
  ]}
  onActiveChange={(id) => track('section_view', { id })}
/>

Props

Generated from the package's own type declarations, so this table cannot drift from the code.

Props for AnchorNav
PropTypeDefaultDescription
itemsAnchorNavItem[]——
labelstring—Accessible name. Defaults to `title` when it is a string, else `'On this page'`.
titleReactNode—Visible heading above the list.
offsetnumber—Pixels covered by a fixed header, so jumps and the active section account for it. Default `0`.
smoothboolean—Animate the jump. Ignored — instant — when the user prefers reduced motion. Default `true`.
activeIdstring—Controlled active `id`.
defaultActiveIdstring——
onActiveChange(id: string) => void——
orientation'vertical' | 'horizontal'——
size'sm' | 'md'——

Every remaining prop is spread onto the root element, so all standard HTML and ARIA attributes work. className and style are merged with the library's own, never replaced, and the ref forwards to the root DOM node.

Rendering