Meowland docs
Workbench

Component reference

NavList

Data-first site navigation: labelled groups of links and actions that own the grouping, the active location and the link/button semantics.

Preview

Documentation sidebar groups

A labelled navigation landmark with grouped links, icons, badges and action rows. The host owns routing; the action rows here only update a local receipt, and the active page is fixed example data.

packages/meowtail/stories/nav-list.stories.tsxline 24tsx
render() {
  const feedback = signal("Action rows report their selection here.");
  return (
    <Measure width="page">
      <Stack style={{ width: "min(100%, 280px)" }} gap="normal">
        <NavList
          label="Documentation"
          activeId="section"
          onSelect={(item) => {
            feedback.value = `Selected the action row: ${String(item.label)}.`;
          }}
          groups={[
            {
              id: "overview",
              label: "Overview",
              items: [
                {
                  id: "home",
                  label: "Home",
                  href: "#nav-list-home",
                  icon: <Icon name="file" />,
                },
                {
                  id: "workbench",
                  label: "Component workbench",
                  href: "#nav-list-workbench",
                  icon: <Icon name="list" />,
                  badge: <Badge size="small">New</Badge>,
                },
              ],
            },
            {
              id: "components",
              label: "Components",
              items: [
                {
                  id: "page",
                  label: "Page",
                  href: "#nav-list-page",
                  icon: <Icon name="file-text" />,
                },
                {
                  id: "section",
                  label: "Section",
                  href: "#nav-list-section",
                  icon: <Icon name="notebook-tabs" />,
                  meta: "12",
                },
                {
                  id: "nav-list",
                  label: "NavList",
                  href: "#nav-list-self",
                  icon: <Icon name="list-checks" />,
                  badge: "Current",
                  description: "Grouped site navigation with link and action rows.",
                },
                {
                  id: "modal",
                  label: "Modal",
                  href: "#nav-list-modal",
                  icon: <Icon name="panel-left" />,
                  badge: "Beta",
                },
              ],
            },
            {
              id: "guides",
              label: "Guides",
              items: [
                {
                  id: "authoring",
                  label: "Authoring pages from regions",
                  href: "#nav-list-authoring",
                  meta: "14 min",
                },
                {
                  id: "adoption",
                  label: "Adopting the components",
                  href: "#nav-list-adoption",
                },
              ],
            },
            {
              id: "actions",
              label: "Actions",
              items: [
                {
                  id: "palette",
                  label: "Open command palette",
                  icon: <Icon name="search" />,
                  meta: "⌘K",
                },
                {
                  id: "settings",
                  label: "Open documentation settings",
                  icon: <Icon name="settings" />,
                  description: "An action row without a link; the host would open its settings.",
                },
              ],
            },
          ]}
        />
        <span role="status">{feedback}</span>
      </Stack>
    </Measure>
  );
}

Installation

bun add meowtail

Usage

usage.tsts
import { NavList } from "meowtail";

Examples

The other stories that show this component.

Long labels in a narrow sidebar

The same public navigation inside a narrow sidebar: long labels and an unbroken identifier wrap instead of widening the column, descriptions stack under the label, and disabled rows keep their place without activation.

packages/meowtail/stories/nav-list.stories.tsxline 150tsx
render() {
  return (
    <Measure width="page">
      <Stack style={{ width: "min(100%, 220px)" }} gap="normal">
        <NavList
          label="Component workspaces"
          activeId="long"
          groups={[
            {
              id: "workspaces",
              label: "Component workspaces in this release",
              items: [
                {
                  id: "long",
                  label:
                    "Navigation item with a long descriptive label that has to wrap inside the sidebar",
                  href: "#nav-list-long",
                  icon: <Icon name="file-text" />,
                  badge: "Updated",
                  description: "The description wraps under the label without widening the list.",
                },
                {
                  id: "unbroken",
                  label: `unbroken-identifier-${"x".repeat(60)}`,
                  href: "#nav-list-unbroken",
                  meta: "12",
                },
                {
                  id: "disabled-link",
                  label: "Disabled anchor with a long label that keeps its place",
                  href: "#nav-list-disabled",
                  disabled: true,
                  badge: "Unavailable",
                },
                {
                  id: "disabled-action",
                  label: "Disabled action",
                  disabled: true,
                  meta: "Locked",
                },
                { id: "open", label: "Open workspace" },
              ],
            },
          ]}
        />
      </Stack>
    </Measure>
  );
}

API reference

The host owns routing, search filtering and what an item points at.

Props

PropTypeDefault
labelRequiredThe navigation landmark name, rendered as the nav element's aria-label.
string–
groupsRequiredThe groups and items to render. Fixed for the mount: the list is built once and a later array change is ignored. The host owns filtering and rebuilding.
readonly NavListGroup[]–
activeIdThe active item id. Pass a Signal to keep aria-current="page" live.
MaybeSignal<string>–
onSelectCalled with the item when an item without href is activated. Items with href navigate through their anchor; routing and search filtering stay with the host.
(item: NavListItem) => void–

Also takes className, children and the attributes and events of its root element.

DOM parts

rootgroupgroupTitlelistitemiconlabelbadgemetadescription

Registry

The component is registered as nav-list in the meowtail component registry.