Skip to content
garn

Navigation Menu

Primary site navigation with optional dropdown panels.

Stablev0.4.2added in v0.1.0@garn/ui/navigation-menu
On this page

Installation

garn is copy-in — the garn CLI writes this component's source into your project, so you own and can edit it. It resolves any base files and installs the npm dependencies below for you.

terminal
npx garn-ui add navigation-menu
First time? Set up garn in your project
Registry
navigation-menu
Deps
@radix-ui/react-navigation-menuclass-variance-authoritylucide-react
Registry deps
utils

Import

import { NavigationMenu, NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink, NavigationMenuIndicator, NavigationMenuViewport, navigationMenuTriggerStyle } from "@garn/ui/navigation-menu";

Anatomy

The parts this component is built from — compose them to assemble it.

NavigationMenuRequired[data-slot="navigation-menu"]

Root — owns which item is open and renders the shared Viewport.

NavigationMenuListRequired[data-slot="navigation-menu-list"]

The horizontal list of top-level items.

NavigationMenuItemRequired[data-slot="navigation-menu-item"]

One top-level entry — a link, or a trigger + content panel.

NavigationMenuTrigger[data-slot="navigation-menu-trigger"]

Opens an item's dropdown panel (with a rotating chevron).

NavigationMenuContent[data-slot="navigation-menu-content"]

The dropdown panel; rendered into the shared Viewport.

NavigationMenuLink[data-slot="navigation-menu-link"]

A navigation link (asChild to wrap your router's Link). Apply navigationMenuTriggerStyle() to match trigger styling.

NavigationMenuIndicator[data-slot="navigation-menu-indicator"]

The little arrow pointing at the active trigger.

NavigationMenuViewport[data-slot="navigation-menu-viewport"]

The shared, animated container all panels render into (rendered by the Root).

navigationMenuTriggerStyle

A cva() class helper (no variants) so plain links can match the trigger's look.

Props

The component's public props and their types.

No component-specific props — this is a thin wrapper over its native element.

Styling

Target these data-slots and states, and remap these tokens, to restyle without forking the component.

[data-slot="navigation-menu"]

States

SelectorState
[data-state=open]open
[data-state=closed]closed
[data-active]active-link
PropertyTokenTier
triggerHeight--garn-control-h-mdsemantic · air
listGap--garn-gap-inlinesemantic · air
motion--garn-motion-basesemantic

When to use

Primary site navigation with optional rich dropdown panels — a top header nav or mega-menu.

Reach for something else when

  • Application command menus (use menubar)
  • a single button-triggered menu (use dropdown-menu)
  • in-page view switching (use tabs)
  • a plain list of links (just use a <nav> + links).

Overview

NavigationMenu is primary site navigation with optional rich dropdown panels — a top header nav or mega-menu. NavigationMenu owns which item is open and hosts a shared Viewport; each NavigationMenuItem is either a direct link or a NavigationMenuTrigger + NavigationMenuContent panel for grouped destinations. It's for navigating to places; for menus of actions use DropdownMenu/Menubar, and for in-page section switching use Tabs.

Guidelines

Use it to navigate, not to run actions. Its items lead to pages/sections; commands belong in a DropdownMenu or Menubar. Keep the top level short and label items by destination, not feature jargon.

Keep dropdown/mega panels scannable. When an item expands a panel, group links under clear headings and keep the panel shallow — a wall of links is as hard to use as no menu. Mark the current section so users know where they are.

Make every destination reachable by keyboard and on mobile. Triggers open on focus/Enter and the panel is navigable; don't gate links behind hover-only reveals. Provide a responsive collapse (a Sheet/drawer) so the nav still works on small screens.

Best practices

Do
  • Use NavigationMenuLink (asChild) for real navigation, and navigationMenuTriggerStyle() so links match triggers.
  • Reserve NavigationMenuTrigger/Content for items that actually reveal a panel.
  • Mark the current destination with active; label the nav if the page has more than one.
Don't
  • Don't use it for in-page tabs or app command bars.
  • Don't put a trigger on items that are just links.
  • Don't bury primary destinations behind triggers unnecessarily.

Content guidelines

  • Use short, destination-named links and keep top-level labels broad and scannable.
  • In mega-menu panels, group links under clear category headings.

Troubleshooting

A simple destination opens an empty/needless dropdown.

Cause. It was built with a Trigger + Content instead of a plain link.

Fix. Use NavigationMenuLink (with navigationMenuTriggerStyle()) for plain destinations; reserve triggers for panels.

Client-side routing doesn't fire from a nav link.

Cause. A raw <a> was used instead of the router's Link.

Fix. Wrap your router's Link with NavigationMenuLink asChild.

Accessibility

Role
navigation
Focus
Triggers and links show a focus ring; panels animate in the shared Viewport.

Keyboard

TabMove between links and triggers.
EnterSpaceArrowDownOpen the focused item's panel.
EscapeClose the open panel.
A1.3.1Info and RelationshipsA2.1.1KeyboardAA2.4.5Multiple WaysA4.1.2Name, Role, Value
  • The Root is a <nav> landmark; items are links or disclosure triggers that reveal aria-controlled panels.
  • Use NavigationMenuLink for real navigation (asChild to wrap a router Link); mark the current page with `active`.
  • Only use a trigger for items that reveal a panel — plain destinations are just links.