Skip to content

Navigation Menu

Stable

Top-level site navigation with rich, animated panels — for marketing sites and documentation where each section needs more than a single link.

Installation

$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/navigation-menu.json

Usage

import Link from "next/link"

import {
  NavigationMenu,
  NavigationMenuContent,
  NavigationMenuItem,
  NavigationMenuLink,
  NavigationMenuList,
  NavigationMenuTrigger,
  navigationMenuTriggerStyle,
} from "@/components/ui/navigation-menu"
<NavigationMenu>
  <NavigationMenuList>
    <NavigationMenuItem>
      <NavigationMenuTrigger>Products</NavigationMenuTrigger>
      <NavigationMenuContent>
        <NavigationMenuLink render={<Link href="/analytics" />}>
          Analytics
        </NavigationMenuLink>
      </NavigationMenuContent>
    </NavigationMenuItem>
    <NavigationMenuItem>
      <NavigationMenuLink
        render={<Link href="/pricing" />}
        className={navigationMenuTriggerStyle()}
      >
        Pricing
      </NavigationMenuLink>
    </NavigationMenuItem>
  </NavigationMenuList>
</NavigationMenu>

All panels share a single popup. When people move from one trigger to the next, the popup morphs to the new panel's size and the content slides in from the direction of travel — a strong spatial cue that they're browsing siblings, not opening something new.

Examples

Mix triggers that open panels with plain links styled by navigationMenuTriggerStyle(), so every item in the bar looks and behaves consistently.

A two-column list of destinations, each with an icon and a one-line description. Descriptions do the heavy lifting: they let people choose without clicking through.

Reserve a panel's side column for one timely item — a launch, a guide, an event. One feature per panel; two features compete.

With no panels, the menu is a styled list of links with roving focus. Mark the current section with active — it sets aria-current="page" and the active style.

Guidelines

When to use

  • For the primary navigation of marketing sites and documentation with a handful of top-level sections.
  • When a section needs orientation — descriptions, grouping or a featured item — before people choose where to go.

When not to use

  • For app navigation with many destinations or deep hierarchies — use a Sidebar.
  • For commands and actions — use a Dropdown Menu or Menubar. Navigation menus contain links only.
  • On small screens — below md, collapse the navigation into a Sheet with a simple list.

Panel content

Keep five to seven top-level items. Every link in a panel gets a short title and, ideally, a one-line description in sentence case — punctuated the same way across the panel. Group long panels into columns of related links, and don't hide anything important only inside a panel — the destination should also be reachable from the page itself or the footer.

AnalyticsUnderstand usage in real time
AutomationsWorkflows that run themselves
Do.Titles people recognize, with descriptions that help them choose.
Pulse
Flow Engine X
Nimbus
Don’t.Marketing names with no context force people to click to find out.

Hover and click

Panels open on hover after a short delay (50ms) and on click. The popup and trigger are joined by an invisible bridge, so the pointer can travel diagonally into a panel without closing it. Never put a destination on the trigger itself — a trigger only opens its panel; the section's overview page belongs inside the panel as its first link.

Accessibility

Navigation Menu renders a <nav> with a list of items. Triggers are buttons with aria-expanded; panels are disclosed content, not menus — links inside are reached with Tab, just like on the page.

KeyBehavior
Tab
Moves through triggers and links, and into an open panel's links.
Moves focus between top-level triggers and links.
EnterSpace
Opens or closes the focused trigger's panel; follows the focused link.
On a trigger, opens its panel and moves focus into it.
Esc
Closes the panel and returns focus to its trigger.
  • Current page. Pass active to the link for the current page or section; it's exposed as aria-current="page".
  • Label multiple navs. If a page has more than one <nav>, give each an aria-label (Main, Footer) so screen reader users can tell them apart.

API reference

The root <nav>. Accepts every prop of the Base UI NavigationMenu.Root.

PropTypeDefault
value

Controlled value of the open item. Non-null means a panel is open.

anynull
defaultValue

Initially open item when uncontrolled.

anynull
onValueChange

Called when the open item changes.

(value: any, details) => voidNo default
delay

Hover delay before opening a panel, in ms.

number50
closeDelay

Delay before closing after the pointer leaves, in ms.

number50
orientation

Layout and arrow-key direction.

"horizontal" | "vertical""horizontal"
align

How the shared popup aligns against the active trigger.

"start" | "center" | "end""start"
PropTypeDefault
value

Identifies the item for controlled usage. Generated when omitted.

anyNo default

Opens its item's panel. Renders a chevron that turns while open. Accepts disabled.

The panel for an item. Size it with a width on its first child; the popup animates between panel sizes.

PropTypeDefault
keepMounted

Keep panel content in the DOM while closed, so crawlers can index its links.

booleanfalse
PropTypeDefault
active

Marks the current page: sets aria-current and the active style.

booleanfalse
closeOnClick

Close the open panel when the link is clicked.

booleanfalse
render

Render a framework link, e.g. <Link href="/pricing" />.

ReactElement | (props, state) => ReactElementNo default

A class generator that makes a plain NavigationMenuLink look exactly like a trigger. Accepts { className } to extend it.