Skip to main content

Navigation Menu

A horizontal navigation bar with click-triggered link panels.

1'use client';
2
3import { CodeIcon, PaintBoardIcon, PuzzleIcon } from '@hugeicons/core-free-icons';
4import { HugeiconsIcon } from '@hugeicons/react';
5import { NavigationMenu } from '@/components/ui/navigation-menu';
6
7const products = [
8 {
9 icon: PuzzleIcon,
10 title: 'Components',
11 description: 'Accessible React primitives, ready to copy.',
12 href: '#',
13 },
14 {
15 icon: PaintBoardIcon,
16 title: 'Themes',
17 description: 'Design tokens for light and dark out of the box.',
18 href: '#',
19 },
20 {
21 icon: CodeIcon,
22 title: 'CLI',
23 description: 'Pull source files straight into your project.',
24 href: '#',
25 },
26];
27
28export function Default() {
29 return (
30 <NavigationMenu className="min-h-64 items-start">
31 <NavigationMenu.Item>
32 <NavigationMenu.Trigger className="text-sm">Products</NavigationMenu.Trigger>
33 <NavigationMenu.Content>
34 {products.map((product) => (
35 <NavigationMenu.Link
36 key={product.title}
37 href={product.href}
38 title={product.title}
39 description={product.description}
40 icon={<HugeiconsIcon icon={product.icon} size={16} strokeWidth={1.6} />}
41 />
42 ))}
43 </NavigationMenu.Content>
44 </NavigationMenu.Item>
45 <a href="#" className="text-muted-foreground hover:text-foreground text-sm transition-colors">
46 Docs
47 </a>
48 <a href="#" className="text-muted-foreground hover:text-foreground text-sm transition-colors">
49 Pricing
50 </a>
51 </NavigationMenu>
52 );
53}

Installation

pnpm dlx nachui add navigation-menu

Anatomy

1import { NavigationMenu } from '@/components/ui/navigation-menu';
1<NavigationMenu>
2 <NavigationMenu.Item>
3 <NavigationMenu.Trigger>Products</NavigationMenu.Trigger>
4 <NavigationMenu.Content>
5 <NavigationMenu.Link
6 href="/components"
7 title="Components"
8 description="Accessible React primitives, ready to copy."
9 />
10 </NavigationMenu.Content>
11 </NavigationMenu.Item>
12 <a href="/docs">Docs</a>
13</NavigationMenu>

Composition

Use the following composition to build a NavigationMenu:
NavigationMenu
├── NavigationMenu.Item
│ ├── NavigationMenu.Trigger
│ └── NavigationMenu.Content
│ └── NavigationMenu.Link
└── (plain links)
The root renders a <nav> and lays its children out in a row, so plain anchors and router links live side by side with dropdown items. Each Item owns its open state: it opens on click, never on hover, so panels only show up when the reader asks for them, and it closes on Escape, on an outside click, or after following a link. The panel carries a small caret pointing back at the trigger that opened it.
NavigationMenu.Link renders an <a> by default. To keep client-side navigation (or locale-aware routing), pass asChild with your framework's Link as the only child — the row content is injected into it and the menu still closes after navigating:
1<NavigationMenu.Link
2 asChild
3 title="Components"
4 description="Accessible React primitives."
5 icon={<HugeiconsIcon icon={PuzzleIcon} size={16} />}
6>
7 <Link href="/docs/elements/ui" />
8</NavigationMenu.Link>

Variants

With badges

Rows accept a badge slot next to the title — and anything else you drop into Content renders as-is, like this non-interactive "coming soon" row:
1'use client';
2
3import { Book02Icon, PuzzleIcon, ServerStack01Icon } from '@hugeicons/core-free-icons';
4import { HugeiconsIcon } from '@hugeicons/react';
5import { Badge } from '@/components/ui/badge';
6import { NavigationMenu } from '@/components/ui/navigation-menu';
7
8const resources = [
9 {
10 icon: PuzzleIcon,
11 title: 'Components',
12 description: 'Free and open source, forever.',
13 badge: { variant: 'success', label: 'Open source' },
14 },
15 {
16 icon: Book02Icon,
17 title: 'Changelog',
18 description: '12 components shipped since v2.13.',
19 badge: { variant: 'info', label: 'New' },
20 },
21 {
22 icon: ServerStack01Icon,
23 title: 'MCP server',
24 description: 'Pull component source into your editor.',
25 badge: { variant: 'warning', label: 'Beta' },
26 },
27] as const;
28
29export function Badges() {
30 return (
31 <NavigationMenu className="min-h-64 items-start">
32 <NavigationMenu.Item>
33 <NavigationMenu.Trigger className="text-sm">Resources</NavigationMenu.Trigger>
34 <NavigationMenu.Content>
35 {resources.map((resource) => (
36 <NavigationMenu.Link
37 key={resource.title}
38 href="#"
39 title={resource.title}
40 description={resource.description}
41 badge={<Badge variant={resource.badge.variant}>{resource.badge.label}</Badge>}
42 icon={<HugeiconsIcon icon={resource.icon} size={16} strokeWidth={1.6} />}
43 />
44 ))}
45 </NavigationMenu.Content>
46 </NavigationMenu.Item>
47 </NavigationMenu>
48 );
49}

API Reference

Renders a <nav> element. Accepts every HTMLElement attribute.
PropTypeDefaultDescription
classNamestring-Additional CSS classes
Wraps one trigger + panel pair and owns its open state.
PropTypeDefaultDescription
classNamestring-Additional CSS classes
A button with a chevron that rotates while the panel is open.
PropTypeDefaultDescription
classNamestring-Additional CSS classes
The floating panel, anchored below the item and animated on open/close.
PropTypeDefaultDescription
classNamestring-Additional CSS classes
PropTypeDefaultDescription
titlestring-Row title (required)
descriptionstring-Muted single-line description
iconReact.ReactNode-Rendered inside the leading icon box
badgeReact.ReactNode-Rendered next to the title
asChildbooleanfalseInject the row into a child routing <Link>
hrefstring-Anchor target when not using asChild
classNamestring-Additional CSS classes
Found something to improve?

Notice a bug, typo, or missing detail on this page? Help us make the documentation better by opening a GitHub issue.

Create an Issue
Ctrl+I