Navigation Menu

Website-style mega-menu with hover-triggered content panels. Used for marketing/site navigation headers.

Installation

pnpm
pnpm dlx @hex-core/cli add navigation-menu

Mega menu with grid

Canonical e-commerce / SaaS pattern: a single trigger opens a panel with a categorized grid of links. Each item composes an icon, title, and one-line description so the panel reads at a glance.

tsx
<NavigationMenu>
  <NavigationMenuList>
    <NavigationMenuItem>
      <NavigationMenuTrigger>Products</NavigationMenuTrigger>
      <NavigationMenuContent>
        <ul className="grid w-[600px] grid-cols-2 gap-3 p-4">
          {[
            { href: "/products/analytics", title: "Analytics", description: "Real-time dashboards and funnels." },
            { href: "/products/auth", title: "Auth", description: "Drop-in social, SSO, and passkeys." },
            { href: "/products/billing", title: "Billing", description: "Subscriptions, invoicing, dunning." },
            { href: "/products/storage", title: "Storage", description: "S3-compatible object storage." },
            { href: "/products/email", title: "Email", description: "Transactional + marketing in one." },
            { href: "/products/edge", title: "Edge", description: "Global functions and KV." },
          ].map((item) => (
            <li key={item.href}>
              <NavigationMenuLink
                href={item.href}
                className="flex flex-col gap-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent focus:bg-accent"
              >
                <div className="flex items-center gap-2">
                  <svg
                    xmlns="http://www.w3.org/2000/svg"
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    strokeWidth="2"
                    strokeLinecap="round"
                    strokeLinejoin="round"
                    className="h-4 w-4 text-muted-foreground"
                    aria-hidden="true"
                  >
                    <rect x="3" y="3" width="7" height="7" rx="1" />
                    <rect x="14" y="3" width="7" height="7" rx="1" />
                    <rect x="3" y="14" width="7" height="7" rx="1" />
                    <rect x="14" y="14" width="7" height="7" rx="1" />
                  </svg>
                  <span className="text-sm font-medium">{item.title}</span>
                </div>
                <p className="line-clamp-2 text-sm text-muted-foreground">{item.description}</p>
              </NavigationMenuLink>
            </li>
          ))}
        </ul>
      </NavigationMenuContent>
    </NavigationMenuItem>
  </NavigationMenuList>
</NavigationMenu>

With viewport indicator

NavigationMenuIndicator renders the small underline arrow that follows the active trigger; the viewport is rendered automatically by <NavigationMenu>.

tsx
<NavigationMenu>
  <NavigationMenuList>
    <NavigationMenuItem>
      <NavigationMenuTrigger>Learn</NavigationMenuTrigger>
      <NavigationMenuContent>
        <ul className="grid w-[280px] gap-2 p-3">
          <li><NavigationMenuLink href="/docs">Documentation</NavigationMenuLink></li>
          <li><NavigationMenuLink href="/guides">Guides</NavigationMenuLink></li>
          <li><NavigationMenuLink href="/changelog">Changelog</NavigationMenuLink></li>
        </ul>
      </NavigationMenuContent>
    </NavigationMenuItem>
    <NavigationMenuItem>
      <NavigationMenuTrigger>Company</NavigationMenuTrigger>
      <NavigationMenuContent>
        <ul className="grid w-[280px] gap-2 p-3">
          <li><NavigationMenuLink href="/about">About</NavigationMenuLink></li>
          <li><NavigationMenuLink href="/careers">Careers</NavigationMenuLink></li>
        </ul>
      </NavigationMenuContent>
    </NavigationMenuItem>
  </NavigationMenuList>
  <NavigationMenuIndicator />
</NavigationMenu>

API Reference

PropTypeDefaultDescription
value
stringControlled active menu value
onValueChange
functionCallback when active menu changes
delayDuration
number200Delay before opening a menu on hover (ms)
orientation
"horizontal" | "vertical"horizontalLayout direction

AI Guidance

When to use

Use for marketing-site top nav with grouped links and mega-menus: Products, Resources, Pricing flyouts. Desktop-first but keyboard accessible.

When not to use

Don't use for app shell menus (use Menubar). Don't use for single dropdowns (use DropdownMenu). On mobile, pair with a separate hamburger/Drawer pattern — NavigationMenu collapses poorly on small screens.

Common mistakes

  • Mixing regular <a> with NavigationMenuLink — must use NavigationMenuLink for keyboard/roving focus
  • Forgetting the viewport — handled automatically when using the composed NavigationMenu root
  • Too many top-level items overflow on mobile

Accessibility

Radix implements the WAI-ARIA menu-button pattern with hover-intent delays and focus trapping in content. Links inside NavigationMenuLink get roving tabindex.

Related components

Token budget: 800