Navbar and Navigation Menu
Navbar and Navigation Menu: anatomy, keyboard behavior, state, and theming in Askr.
Example
import { NavigationMenu, NavigationMenuItem, NavigationMenuLink, NavigationMenuList } from '@askrjs/themes/components';
<NavigationMenu aria-label="Primary navigation">
<NavigationMenuList>
<NavigationMenuItem><NavigationMenuLink href={projectsRoute}>Projects</NavigationMenuLink></NavigationMenuItem>
<NavigationMenuItem><NavigationMenuLink href={reportsRoute}>Reports</NavigationMenuLink></NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>Purpose
Despite the page title, the only export at `@askrjs/themes/navigation-menu` is `NavigationMenu` and its parts — there's no separate `Navbar` primitive on this subpath. Use these pieces to build a horizontal menu bar with flyout content: a top-level list of items, each optionally opening a panel of links. If you actually want the responsive app-header component (with a collapsing hamburger state), that's `Navbar` from the general themes component barrel, not this one.
Install and import
```tsx import { NavigationMenu, NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink, NavigationMenuViewport, NavigationMenuIndicator, } from '@askrjs/themes/navigation-menu'; ``` This subpath is an alias into the shared components barrel, so importing the same names from `@askrjs/themes/components` works identically.
Live examples
The example composes `NavigationMenu` with a `NavigationMenuList` of `NavigationMenuItem`/`NavigationMenuLink` pairs, and passes route destinations rather than hand-written path strings. Note the `aria-label` on the menu itself: with more than one navigation landmark on a page, each needs its own name for that landmark list to be usable.
Anatomy
`NavigationMenu` renders a `<nav>`, `NavigationMenuList` an `<ul>`, and `NavigationMenuItem` an `<li>` — a plain nested list under the hood. `NavigationMenuTrigger` is a real `<button type="button">` (with the `nav-item` class baked in), while `NavigationMenuContent`, `NavigationMenuLink`, `NavigationMenuViewport`, and `NavigationMenuIndicator` are generic slotted elements (a div, an anchor, a div, and a div respectively) that only exist to give you stable markup and `data-slot` hooks for the flyout panel, its links, the clipping viewport, and an active-tab indicator strip.
State model
None of these components track open/closed state, active item, or hover intent — there's no internal store or context here at all. You decide which trigger's content is visible, typically with a bit of local state or by composing `NavigationMenuTrigger`/`NavigationMenuContent` with the interaction primitives from `@askrjs/ui`. The themed `Navbar` gives you two different worked examples of that pattern, for two different jobs: its mobile hamburger collapse is built on `Collapsible`/`CollapsibleTrigger`/`CollapsibleContent`, while a per-item flyout (`NavDropdown`) is built on `Dropdown`/`DropdownTrigger`/`DropdownContent` — pick whichever matches what you're building rather than assuming one mechanism covers both. Treat this package as markup, not behavior.
Keyboard and accessibility
Because `NavigationMenu` is a `<nav>` and `NavigationMenuTrigger` is a genuine `<button>`, you get landmark navigation and native Tab/Enter/Space activation for free. What you don't get automatically is `aria-expanded`, `aria-controls`, or Escape-to-close on the trigger/content pair, or arrow-key movement across items — those are your responsibility to wire up if you're building a real dropdown-style flyout rather than a flat link list.
Styling and tokens
Every part carries a `data-slot` attribute — `navigation-menu`, `navigation-menu-list`, `navigation-menu-item`, `navigation-menu-trigger`, `navigation-menu-content`, `navigation-menu-link`, `navigation-menu-viewport`, `navigation-menu-indicator` — and the trigger additionally gets the `nav-item` class. Style off those hooks instead of assuming a fixed DOM shape, since `as`/`asChild` let consumers swap the underlying element.
API
`NavigationMenu`, `NavigationMenuList`, and `NavigationMenuItem` accept the shared `CatalogComponentProps` shape, including `as` to render a different tag. `NavigationMenuTrigger` is built on the same internal button-part helper as the rest of the catalog's button-flavored components, and that helper accepts `asChild` but silently discards `as` — passing it has no effect, it always renders a real `<button>` unless you use `asChild`. `asChild` itself hands rendering off to a `Slot` (useful for wrapping your router's `Link`) on every part, plus `class`, `children`, `ref`, and arbitrary passthrough attributes are supported throughout. `NavigationMenuTrigger` also accepts a `type` override, though it defaults to `"button"` so it never accidentally submits a form.
Edge cases
Multi-level or mega-menu content is entirely on you to lay out inside `NavigationMenuContent` — there's no built-in positioning or collision handling in `NavigationMenuViewport`, it's just a slotted div. Since there's no shipped open state, SSR is trivially safe (everything renders closed/static by default), but that also means client hydration has to attach whatever interaction logic you add. If you need real click-outside dismissal or focus trapping for a flyout, reach for `@askrjs/ui`'s dropdown primitives rather than assuming this package provides it.
Related pages
See Sidebar and Menubar for the other two navigation-chrome building blocks, Application Chrome for how these fit into a full page shell, and Breadcrumb and Pagination for the smaller wayfinding controls that often sit alongside a nav bar.