Nav List
Primitive

Nav List

Headless navigation list primitives for sidebars, page navigation, and grouped route lists.

Live behavior

Nav List in motion

Interactive
Atom behavior · App-owned appearance
Preparing behavior…

Interact with the specimen and inspect the behavior Atom contributes.

waiting for input

When to Use

Use NavList for an ordinary list of destinations, such as sidebar links, settings pages, or grouped documentation routes. Use NavigationMenu when a top-level link opens a panel of more links, Menubar for application commands, and Listbox when the rows select values instead of navigating.

Features

The behavior Atom owns before your product adds appearance.

  • Renders native navigation, list, list item, link, section, heading, and button semantics.
  • Supports vertical and horizontal orientation metadata for styled layers.
  • Supports active/current link state with configurable aria-current tokens.
  • Supports disabled links and disabled section interaction.
  • Supports collapsible sections with controlled and uncontrolled open state.
  • Keeps closing section content mounted through exit motion and exposes measured content-size hooks for styled disclosure animation.
  • Supports asChild and render composition across every rendered part.
  • Keeps route behavior in consumer links while Atom owns ARIA and data attributes.

Import

tsx
import { NavList } from "@flowstack-ui/atom";

Anatomy

tsx
<NavList.Root>
  <NavList.List>
    <NavList.Item>
      <NavList.Link />
    </NavList.Item>
  </NavList.List>
  <NavList.Section>
    <NavList.SectionLabel />
    <NavList.SectionTrigger />
    <NavList.SectionContent>
      <NavList.List>
        <NavList.Item>
          <NavList.Link />
        </NavList.Item>
      </NavList.List>
    </NavList.SectionContent>
  </NavList.Section>
</NavList.Root>

API Reference

All DOM-rendering parts accept native props for their default element, except for props Atom owns for behavior or accessibility. data-slot can be overridden as a native data-* prop, but it is documented under data attributes because it is primarily a styling hook.

Root

Contains the navigation list and provides orientation metadata to descendants. Renders a nav by default.

PropTypeDefault
childrenReactNode-
orientation"vertical" | "horizontal""vertical"
asChildbooleanfalse
renderRenderProp-
Data attributeValues
[data-slot]"nav-list" by default
[data-orientation]"vertical" | "horizontal"

List

Renders the item list as ul by default, or ol when ordered is true.

PropTypeDefault
childrenReactNode-
orderedbooleanfalse
asChildbooleanfalse
renderRenderProp-
Data attributeValues
[data-slot]"nav-list-list" by default
[data-orientation]"vertical" | "horizontal"
[data-ordered]Present when ordered is true

Item

Wraps one navigation item. Renders an li by default.

PropTypeDefault
childrenReactNode-
disabledbooleanfalse
asChildbooleanfalse
renderRenderProp-
Data attributeValues
[data-slot]"nav-list-item" by default
[data-orientation]"vertical" | "horizontal"
[data-disabled]Present when disabled

Item disabled is a styling hook only. Pass disabled to NavList.Link to prevent navigation.

Renders a native link or composed route link. Renders an a by default.

PropTypeDefault
childrenReactNode-
hrefstring-
activebooleanfalse
currentboolean | "page" | "step" | "location" | "date" | "time""page"
disabledbooleanfalse
asChildbooleanfalse
renderRenderProp-
aria-currentboolean | "page" | "step" | "location" | "date" | "time"active current value
ARIA attributeValues
aria-currentFrom aria-current, or from current when active
aria-disabledPresent when disabled
Data attributeValues
[data-slot]"nav-list-link" by default
[data-orientation]"vertical" | "horizontal"
[data-active]Present when active
[data-current]Present when active
[data-disabled]Present when disabled

When disabled is true, href is omitted, clicks are prevented, and tabIndex={-1} is rendered. aria-current overrides the value derived from active and current.

Section

Provides grouped navigation state. Renders a section by default.

PropTypeDefault
childrenReactNode-
collapsiblebooleanfalse
openboolean-
defaultOpenbooleantrue
onOpenChange(open: boolean) => void-
disabledbooleanfalse
asChildbooleanfalse
renderRenderProp-
Data attributeValues
[data-slot]"nav-list-section" by default
[data-orientation]"vertical" | "horizontal"
[data-state]"open" | "closed"
[data-collapsible]Present when collapsible
[data-disabled]Present when disabled

Non-collapsible sections are always open. disabled prevents collapsible sections from changing open state.

SectionLabel

Labels a section. Renders h2 by default and registers itself so section content can reference the label.

PropTypeDefault
childrenReactNode-
as"h2" | "h3" | "h4" | "h5" | "h6" | "div""h2"
asChildbooleanfalse
renderRenderProp-
Data attributeValues
[data-slot]"nav-list-section-label" by default
[data-orientation]"vertical" | "horizontal"

SectionTrigger

Toggles a collapsible section. Renders a button by default. When composed with a non-button element, Atom adds button semantics and keyboard activation.

PropTypeDefault
childrenReactNode-
onClickMouseEventHandler<HTMLElement>-
onKeyDownKeyboardEventHandler<HTMLElement>-
asChildbooleanfalse
renderRenderProp-
ARIA attributeValues
aria-expandedSection open state when collapsible
aria-controlsSection content id when collapsible
aria-disabledPresent for disabled non-native composed triggers
Data attributeValues
[data-slot]"nav-list-section-trigger" by default
[data-orientation]"vertical" | "horizontal"
[data-state]"open" | "closed"
[data-collapsible]Present when the section is collapsible
[data-disabled]Present when the section is disabled

For non-collapsible sections, the trigger remains rendered but does not expose expanded state or toggle behavior.

SectionContent

Contains section links. Renders a div by default.

PropTypeDefault
childrenReactNode-
forceMountbooleanfalse
asChildbooleanfalse
renderRenderProp-
ARIA attributeValues
aria-labelledbySection label id, or trigger id for collapsible sections without a label
Data attributeValues
[data-slot]"nav-list-section-content" by default
[data-orientation]"vertical" | "horizontal"
[data-state]"open" | "closed"
[data-collapsible]Present when the section is collapsible
[data-initial-open]Present while an initially open section has not transitioned

During a transition, SectionContent exposes --content-height and --content-width and remains mounted until a styled exit animation completes. When forceMount is true, closed content becomes hidden after that animation.

Advanced compound components can use useNavListContext and useNavListSectionContext; the matching providers and context value types are also public exports.

Examples

tsx
import { NavList } from "@flowstack-ui/atom";

export function ComponentsNavigation() {
  return (
    <NavList.Root aria-label="Components">
      <NavList.Section collapsible defaultOpen>
          <NavList.SectionLabel>Components</NavList.SectionLabel>
          <NavList.SectionTrigger>Show or hide components</NavList.SectionTrigger>
          <NavList.SectionContent>
            <NavList.List>
              <NavList.Item>
                <NavList.Link active current="page" href="/components/input">
                  Input
                </NavList.Link>
              </NavList.Item>
              <NavList.Item>
                <NavList.Link href="/components/select">Select</NavList.Link>
              </NavList.Item>
            </NavList.List>
          </NavList.SectionContent>
      </NavList.Section>
    </NavList.Root>
  );
}

Ordered Steps

tsx
import { NavList } from "@flowstack-ui/atom";

export function SetupSteps() {
  return (
    <NavList.Root orientation="horizontal" aria-label="Setup steps">
      <NavList.List ordered>
        <NavList.Item>
          <NavList.Link active current="step" href="/setup/account">
            Account
          </NavList.Link>
        </NavList.Item>
        <NavList.Item>
          <NavList.Link href="/setup/profile">Profile</NavList.Link>
        </NavList.Item>
      </NavList.List>
    </NavList.Root>
  );
}

Accessibility

NavList uses native nav, list, list item, heading, button, and anchor behavior. It does not implement roving focus because route navigation should remain normal Tab navigation.

Active links expose aria-current using the current token, defaulting to "page". Use current="step", "location", "date", or "time" when the current item represents that ARIA current state instead of a page.

KeyDescription
TabMoves through links and section triggers in document order
EnterActivates a focused link or section trigger
SpaceToggles a focused section trigger

Changelog

Unreleased

  • Added public Agent Knowledge for component selection, required composition, recurring mistakes, and validation.

0.1.0

  • Added native navigation list, collapsible section, and active link primitives.