Navigation Menu
A composable navigation menu system built on Radix UI primitives. It provides a flexible, accessible way to create navigation bars with dropdown menus, animated transitions, and keyboard navigation support.
Usage
Simple Navigation
Basic navigation with links only:
import {
NavigationMenu,
NavigationMenuList,
NavigationMenuItem,
NavigationMenuLink,
} from "@e-infra/design-system";
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuLink href="/dashboard">Dashboard</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink href="/projects">Projects</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink href="/team">Team</NavigationMenuLink>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>;Navigation with Dropdowns
Create dropdown menus using NavigationMenuTrigger and NavigationMenuContent:
import {
NavigationMenu,
NavigationMenuList,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuTrigger,
NavigationMenuContent,
NavigationMenuIndicator,
} from "@e-infra/design-system";
import { BarChart3, Users } from "lucide-react";
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuLink href="/dashboard">Dashboard</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Analytics</NavigationMenuTrigger>
<NavigationMenuContent>
<div className="grid gap-3 p-4 w-[400px]">
<div className="flex items-center gap-4">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-muted">
<BarChart3 className="h-5 w-5" />
</div>
<div>
<div className="text-sm font-medium">Overview</div>
<div className="text-xs text-muted-foreground">
View your analytics dashboard
</div>
</div>
</div>
<div className="flex items-center gap-4">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-muted">
<Users className="h-5 w-5" />
</div>
<div>
<div className="text-sm font-medium">Users</div>
<div className="text-xs text-muted-foreground">
Manage user analytics
</div>
</div>
</div>
</div>
</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
{/* Optional: Shows an arrow pointing to the active trigger */}
<NavigationMenuIndicator />
</NavigationMenu>;Custom Viewport Positioning
By default, the viewport is automatically rendered. Disable it for custom positioning:
import {
NavigationMenu,
NavigationMenuList,
NavigationMenuViewport,
} from "@e-infra/design-system";
<NavigationMenu viewport={false}>
<NavigationMenuList>...</NavigationMenuList>
<NavigationMenuViewport className="custom-position" />
</NavigationMenu>;Component Breakdown
NavigationMenu (Root)
The outermost wrapper. It manages the overall state of the menu and optionally renders the floating Viewport. The viewport prop controls whether the dropdown content appears in a detached floating panel or inline.
| Prop | Type | Default | Description |
|---|---|---|---|
| viewport | boolean | true | Whether to automatically render the floating viewport |
| className | string | - | Additional CSS classes |
| children | React.ReactNode | - | Navigation content |
NavigationMenuList
A horizontal flex container that holds all the top-level menu items. Think of it as the navbar row — it spaces items evenly and handles the list semantics.
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | - | Additional CSS classes |
| children | React.ReactNode | - | Navigation items |
NavigationMenuItem
Wraps each individual nav entry. It adds a subtle animated underline on hover via a CSS after: pseudo-element that grows from 0 to full width.
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | - | Additional CSS classes |
| children | React.ReactNode | - | Item content (link or trigger) |
NavigationMenuTrigger
A button that opens a dropdown submenu. It renders a ChevronDownIcon that rotates 180° when the menu is open. Uses NavigationMenuTriggerStyle (a CVA variant) so the same base styles can be reused elsewhere without the trigger behavior.
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | - | Additional CSS classes |
| children | React.ReactNode | - | Trigger label |
NavigationMenuContent
The dropdown panel that appears when a trigger is activated. It handles enter/exit animations (slide + fade) depending on which direction the user navigates between menu items (left/right).
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | - | Additional CSS classes |
| children | React.ReactNode | - | Dropdown content |
NavigationMenuViewport
A detached floating container anchored below the navbar. When viewport={true} on the root, all Content panels are portaled into this single element — this is what enables the smooth shared-height animation as you move between items.
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | - | Additional CSS classes |
NavigationMenuLink
A styled anchor/link element for use inside content panels or directly in items. It mirrors the underline hover effect from NavigationMenuItem and supports an active state with a tinted background.
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | - | Additional CSS classes |
| href | string | - | Link destination |
| active | boolean | false | Whether the link is active |
| children | React.ReactNode | - | Link content |
NavigationMenuIndicator
A small decorative arrow/caret that appears above the viewport, pointing up toward the active trigger. It fades in/out and visually connects the trigger to the dropdown below it.
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | - | Additional CSS classes |
NavigationMenuTriggerStyle
Not a component — it's a reusable style factory using class-variance-authority. Exported separately so you can apply the same trigger appearance to a plain <Link> or custom element without wrapping it in NavigationMenuTrigger.
import { NavigationMenuTriggerStyle } from "@e-infra/design-system";
// Use for custom triggers
<button className={NavigationMenuTriggerStyle()}>
Custom Trigger
</button>
// Or extend with variants
import { cva } from "class-variance-authority";
const CustomTriggerStyle = cva(NavigationMenuTriggerStyle(), {
variants: {
variant: {
default: "",
bold: "font-bold",
},
},
});Composition Structure
NavigationMenu ← state + viewport toggle
├── NavigationMenuList ← horizontal row
│ ├── NavigationMenuItem ← single nav slot
│ │ ├── NavigationMenuTrigger ← opens dropdown
│ │ └── NavigationMenuContent ← dropdown body
│ └── NavigationMenuItem
│ └── NavigationMenuLink ← direct link (no dropdown)
├── NavigationMenuIndicator ← caret pointer (optional)
└── NavigationMenuViewport ← floating panel host (auto)Animation Details
| Animation | Trigger | Description |
|---|---|---|
| Underline grow | Hover on Item/Link | after: pseudo-element expands from 0 to full width |
| Chevron rotate | Menu open/close | Chevron icon rotates 180° when dropdown opens |
| Content slide | Navigation direction | Slides from left/right based on navigation direction |
| Content fade | Menu open/close | Fade in/out animation for dropdown content |
| Viewport zoom | Menu open/close | Viewport scales in/out when opening/closing |
| Indicator fade | Menu open/close | Indicator fades in/out above active trigger |
Accessibility
- Full keyboard navigation support (arrow keys, Enter, Escape)
- ARIA attributes managed by Radix UI primitives
- Focus trapping within open dropdowns
- Screen reader announcements for menu state changes