Skip to content

Navigation Menu

Site navigation along the top of a page: plain links, and triggers that open a shared panel of grouped links (a mega-menu). Built from NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent and NavigationMenuLink. It is a set of links, not a menu of actions.

Navigationv0.1.0experimentalWCAG 2.2 AAView spec
import { NavigationMenu, NavigationMenuContent, NavigationMenuItem, NavigationMenuLink, NavigationMenuTrigger } from "@rdloom/react";

export default function NavigationMenuBasicExample() {
  return (
    <div className="flex min-h-[22rem] w-full items-start justify-center">
      <NavigationMenu label="Main">
        <NavigationMenuItem>
          <NavigationMenuTrigger>Product</NavigationMenuTrigger>
          <NavigationMenuContent>
            <div className="grid w-80 gap-1">
              <NavigationMenuLink href="#analytics" title="Analytics" description="Track visits, sign-ups and revenue in one place." />
              <NavigationMenuLink href="#automations" title="Automations" description="Run routine steps without doing them by hand." />
              <NavigationMenuLink href="#integrations" title="Integrations" description="Connect the tools your team already uses." />
            </div>
          </NavigationMenuContent>
        </NavigationMenuItem>
        <NavigationMenuItem>
          <NavigationMenuTrigger>Resources</NavigationMenuTrigger>
          <NavigationMenuContent>
            <div className="grid w-80 gap-1">
              <NavigationMenuLink href="#guides" title="Guides" description="Step-by-step help for common tasks." />
              <NavigationMenuLink href="#changelog" title="Changelog" description="What changed in each release." />
            </div>
          </NavigationMenuContent>
        </NavigationMenuItem>
        <NavigationMenuItem>
          <NavigationMenuLink href="#pricing">Pricing</NavigationMenuLink>
        </NavigationMenuItem>
      </NavigationMenu>
    </div>
  );
}

Installation

npx rdloom add navigation-menu

Copies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes. It needs react-aria-components; add --install to install them.

Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/navigation-menu.json

Works, but without upgrade tracking.

Usage

import { NavigationMenu, NavigationMenuContent, NavigationMenuItem, NavigationMenuLink, NavigationMenuTrigger } from "@rdloom/react";

<div className="flex min-h-[22rem] w-full items-start justify-center">
  <NavigationMenu label="Main">
    <NavigationMenuItem>
      <NavigationMenuTrigger>Product</NavigationMenuTrigger>
      <NavigationMenuContent>
        <div className="grid w-80 gap-1">
          <NavigationMenuLink href="#analytics" title="Analytics" description="Track visits, sign-ups and revenue in one place." />
          <NavigationMenuLink href="#automations" title="Automations" description="Run routine steps without doing them by hand." />
          <NavigationMenuLink href="#integrations" title="Integrations" description="Connect the tools your team already uses." />
        </div>
      </NavigationMenuContent>
    </NavigationMenuItem>
    <NavigationMenuItem>
      <NavigationMenuTrigger>Resources</NavigationMenuTrigger>
      <NavigationMenuContent>
        <div className="grid w-80 gap-1">
          <NavigationMenuLink href="#guides" title="Guides" description="Step-by-step help for common tasks." />
          <NavigationMenuLink href="#changelog" title="Changelog" description="What changed in each release." />
        </div>
      </NavigationMenuContent>
    </NavigationMenuItem>
    <NavigationMenuItem>
      <NavigationMenuLink href="#pricing">Pricing</NavigationMenuLink>
    </NavigationMenuItem>
  </NavigationMenu>
</div>
import { NavigationMenu, NavigationMenuContent, NavigationMenuItem, NavigationMenuLink, NavigationMenuTrigger } from "@rdloom/react";

export default function NavigationMenuFeaturedAndGridExample() {
  return (
    <div className="flex min-h-[24rem] w-full items-start justify-center">
      <NavigationMenu label="Main" align="center">
        <NavigationMenuItem>
          <NavigationMenuTrigger>Solutions</NavigationMenuTrigger>
          <NavigationMenuContent>
            <div className="grid w-[34rem] max-w-full grid-cols-[1fr_1.4fr] gap-2">
              <NavigationMenuLink href="#overview" isFeatured title="Solutions overview" description="See how teams of different sizes use the platform." />
              <div className="grid grid-cols-2 gap-1">
                <NavigationMenuLink href="#startups" title="Startups" description="Launch quickly." />
                <NavigationMenuLink href="#agencies" title="Agencies" description="Manage many clients." />
                <NavigationMenuLink href="#enterprise" title="Enterprise" description="Control and audit." />
                <NavigationMenuLink href="#education" title="Education" description="Courses and cohorts." />
              </div>
            </div>
          </NavigationMenuContent>
        </NavigationMenuItem>
        <NavigationMenuItem>
          <NavigationMenuLink href="#docs">Docs</NavigationMenuLink>
        </NavigationMenuItem>
        <NavigationMenuItem>
          <NavigationMenuLink href="#blog">Blog</NavigationMenuLink>
        </NavigationMenuItem>
      </NavigationMenu>
    </div>
  );
}

With current page

import { NavigationMenu, NavigationMenuContent, NavigationMenuItem, NavigationMenuLink, NavigationMenuTrigger } from "@rdloom/react";

export default function NavigationMenuWithCurrentPageExample() {
  return (
    <div className="flex min-h-[22rem] w-full items-start justify-center">
      <NavigationMenu label="Main">
        <NavigationMenuItem>
          <NavigationMenuLink href="#home">Home</NavigationMenuLink>
        </NavigationMenuItem>
        <NavigationMenuItem>
          <NavigationMenuTrigger isCurrent>Guides</NavigationMenuTrigger>
          <NavigationMenuContent>
            <div className="grid w-80 gap-1">
              <NavigationMenuLink href="#start" title="Getting started" description="Install and build your first page." />
              <NavigationMenuLink href="#theming" title="Theming" description="Colors, type and spacing through tokens." isCurrent />
              <NavigationMenuLink href="#testing" title="Testing" description="Check keyboard and screen reader behavior." />
            </div>
          </NavigationMenuContent>
        </NavigationMenuItem>
        <NavigationMenuItem>
          <NavigationMenuLink href="#reference">Reference</NavigationMenuLink>
        </NavigationMenuItem>
      </NavigationMenu>
    </div>
  );
}

API Reference

Defined by the spec. Components also accept the props of the React Aria component they wrap.

PropTypeDefault
label

The accessible name of the navigation landmark, e.g. 'Main'. Give each nav on a page a different name.

string"Main"
delay

Milliseconds the pointer rests on a trigger before its panel opens, and before it closes after the pointer leaves. Keyboard and click open at once.

number150
align

Where an open panel sits under the bar: start aligns it with the bar start, center puts it in the middle of the bar, end aligns it with the bar end. Use center or end for a wide panel near the edge of the page.

"start" | "center" | "end""start"
childrenrequired

NavigationMenuItem elements. Each holds either a NavigationMenuLink, or a NavigationMenuTrigger with a NavigationMenuContent.

nodenone

Accessibility

Role navigation, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.

Keyboard

  • Tab moves through the top-level triggers and links, then into the open panel's links, then on
  • Enter or Space on a trigger opens or closes its panel
  • Down Arrow on a trigger opens its panel and moves to the first link
  • Left and Right Arrow move between the top-level triggers and links (reversed in right-to-left text)
  • Home and End move to the first and last top-level item
  • Esc closes the open panel and returns focus to its trigger

Screen readers announce

  • The landmark is announced as navigation with its name
  • A trigger is announced as a button, expanded or collapsed
  • Links in the panel are read as links with their title and description, and the current one as current page

What your code must do

  • A nav landmark with an accessible name
  • Each trigger is a button with aria-expanded and aria-controls pointing at its panel
  • The panel closes on a click outside it and when focus leaves the navigation
  • Hover opens a panel after a short delay, and everything hover does is also available by keyboard and click
  • The current page is marked with aria-current="page" and a visible mark that is not color alone
  • Opening and closing respect reduced motion

Guidelines

Use it when

  • A marketing or documentation site with several sections, some with many links
  • A header where a few links need a short description each

Avoid it when

  • Actions such as Copy or Delete: use Menu
  • A desktop application's File, Edit, View bar: use Menubar
  • Navigation inside an app with many levels: use Sidebar
  • Five or fewer links with no groups: plain links are enough

Don't

  • Opening a panel only on hover with no click or keyboard route
  • A trigger that is also a link to a page: the trigger only opens the panel
  • Panels with more than about eight links without grouping
  • Putting forms or other controls in the panel

Design tokens

The semantic tokens this component uses. Change them once and every component follows; see Design tokens.

  • --rd-color-surface-default
  • --rd-color-surface-raised
  • --rd-color-surface-subtle
  • --rd-color-border-default
  • --rd-color-text-default
  • --rd-color-text-muted
  • --rd-color-focus-ring
  • --rd-radius-control
  • --rd-radius-overlay