Doc Nav
A manifest-driven, grouped/collapsible, multi-product-aware documentation navigation — a pinned Shared set beside accordion product groups and an Appendix for ad-hoc docs. Copy it into your project and own it completely.
Basic
When to use
Use DocNav for a documentation shell that groups docs by product or section and collapses them, all driven by a docs manifest. It turns a pre-filtered manifest slice into peer, collapsible groups: a pinned Shared set that never leaves view, accordion product groups (opening one collapses the sibling), and an Appendix bucket for ad-hoc docs. The group row wraps — it is never an overflow-x scroll strip.
This is distinct from SectionNav (in-app section sub-navigation with count pills) and Navbar (top-level site navigation with brand and actions). Reach for DocNav when you are rendering a docs index from a manifest. The surrounding themed page shell, sticky header, and brand/logo slot are the sibling DocFrame component.
Anatomy
Each DocEntry in docs carries an order, a label, an href, and optional kind / scope / group / tier / external fields. DocNav derives the groups:
- Shared / pinned — groups whose id is in
pinnedGroups(default["shared"]) stay open regardless of the accordion. - Product groups — accordion peers driven by
activeProduct; opening one collapses the sibling. Toggling callsonProductToggle(id)so the parent can swap the open product. - Appendix — any doc with no
group, noscope, andorder >= 10collapses into an Appendix at the tail. A lone ad-hoc doc renders inline; two or more collapse. - Auto-hide — a group with zero docs in the slice never renders.
The active pill (and its group's auto-expand) resolve from currentPath versus each entry's href, and the active link is marked aria-current="page". External entries badge and open in a new tab.
Single-product mode
Omit activeProduct (and onProductToggle) and the nav degrades to one grouped row with no accordion — the drop-in path for single-product projects. Groups become plain, independently expandable links.
<DocNav docs={docs} currentPath="/docs/cujs" defaultCollapsed={false} />Installation
npx visor add doc-navUsage
import * as React from 'react';
import { DocNav, type DocEntry } from '@/components/ui/doc-nav/doc-nav';
const docs: DocEntry[] = [
{ order: 1, label: 'Charter', href: '/docs/charter.html', group: 'Shared' },
{ order: 3, label: 'Screens', href: '/docs/artist/screens', scope: ['artist'], group: 'Artist' },
{ order: 2, label: 'Journeys', href: '/docs/pro/journeys', scope: ['pro'], group: 'Pro' },
{ order: 11, label: 'Q3 Audit', href: '/docs/q3-audit.html' },
];
export function Nav() {
const [product, setProduct] = React.useState('artist');
return (
<DocNav
docs={docs}
currentPath="/docs/artist/screens"
activeProduct={product}
onProductToggle={setProduct}
/>
);
}API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
docs* | DocEntry[] | — | The manifest slice for the active view (Shared + product-scoped), pre-filtered by DocFrame. Each DocEntry carries order, label, href, and optional kind/scope/group/tier/external. |
currentPath* | string | — | The active route (or static location.pathname). Resolves the active pill (aria-current="page") and auto-expands its group. |
activeProduct | string | — | Which product group is open (the accordion). Absent → single-product mode with no accordion. |
onProductToggle | (id: string) => void | — | Accordion callback — the parent expands a product group and collapses the sibling by swapping activeProduct. Absent → plain, independently expandable links (static-doc mode). |
pinnedGroups | string[] | ["shared"] | Group ids that stay open regardless of the accordion. |
defaultCollapsed | boolean | true | Whether non-active, non-pinned groups start collapsed — the anti-wall rule. |
Doc Frame
The themed doc-page shell that wraps DocNav — a sticky header with a flexible brand/logo slot, the nav slot, and a content wrapper for the doc. Copy it into your project and own it completely.
Menubar
A horizontal menu bar with dropdown menus, keyboard navigation, and support for checkboxes and radio items. Built on Radix UI — copy it into your project and own it completely.