shadcn.io is not affiliated with official shadcn/ui
Links for the site sit in one row. Some open a panel of related pages, and some are just a link.
Use it for destinations in a header. Commands such as Export or Delete belong in a Dropdown Menu. In React, Base UI renders a navigation landmark, and router links go in the link part so the focus behavior stays.
Installation
Anatomy

NavigationMenu
├── NavigationMenuList
│ ├── NavigationMenuItem
│ │ ├── NavigationMenuTrigger
│ │ └── NavigationMenuContent
│ │ └── NavigationMenuLink
│ └── NavigationMenuItem
│ └── NavigationMenuLink
└── NavigationMenuIndicatorUsage
import {
NavigationMenu, NavigationMenuContent, NavigationMenuItem,
NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger,
} from "@/components/ui/navigation-menu";<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Item One</NavigationMenuTrigger>
<NavigationMenuContent>
<NavigationMenuLink href="#">Link</NavigationMenuLink>
</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>Accessibility
- Enter
- Activate the focused control.
- Space
- Activate the focused control.
- Arrow Left
- Move left.
- Arrow Right
- Move right.
- EscEscape
- Close the open panel.
Demos
Rtl
The header in Arabic: a getting-started list, a component grid that wraps into columns, and a docs link with no panel. dir is rtl on the wrapper, so the triggers run from the right and panels follow the reading order. Arrow keys follow that order too. Set direction on the container you ship, and check that a wide panel does not spill off the far edge.
FAQ
How do I use router links in the React Navigation Menu?
Render your router's link through NavigationMenuLink, so it keeps the menu's styles and focus handling.
Pass it as render: <NavigationMenuLink render={<Link href="/docs" />}>Docs</NavigationMenuLink>.
How do I highlight the current page in the Navigation Menu?
Set aria-current="page" on the link that matches the current route. Screen readers announce it, and you can style it with Tailwind's aria-[current=page]: variant.
active on NavigationMenuLink does both for you.
How do I change how fast the Base UI Navigation Menu opens on hover?
Set delay and closeDelay on NavigationMenu, in milliseconds (50 each by default). Raise them if panels open while people only pass over a trigger.
How do I control which Navigation Menu panel is open in React?
Pass value and onValueChange on NavigationMenu: value is the open item's value, and null closes every panel. closeOnClick on a NavigationMenuLink closes it when that link is chosen.
How do I make the Navigation Menu work on mobile?
Hide the bar below md and put the same links in a Sheet behind a menu button. Wide panels under a header bar don't suit small touch screens, and a sheet gives every link a full-width tap target.
When should I use a Navigation Menu instead of a Dropdown Menu or a Menubar?
Use the Navigation Menu for site navigation: links in a header, grouped under triggers. A Dropdown Menu holds actions like Export or Delete, and a Menubar is an app's File, Edit, View bar. Each gives assistive tech the roles that match what it does.
Is the Base UI Navigation Menu accessible?
Yes. It renders a nav landmark, Tab moves between triggers and links, Enter or Space opens a trigger's panel, and Escape closes it and returns focus to the trigger. Give each link text that makes sense on its own, since screen readers can list them out of context.
API Reference
NavigationMenu
<nav> data-slot="navigation-menu"
value?Value | null
The controlled value of the navigation menu item that should be currently open.
When non-nullish, the menu will be open. When nullish, the menu will be closed.
To render an uncontrolled navigation menu, use the defaultValue prop instead.
- Type
Value | null- Default
null
defaultValue?Value | null
The uncontrolled value of the item that should be initially selected.
To render a controlled navigation menu, use the value prop instead.
- Type
Value | null- Default
null
onValueChange?(value: Value | null, eventDetails: NavigationMenuRoot.ChangeEventDetails) → void
Callback fired when the value changes.
- Type
(value: Value | null, eventDetails: NavigationMenuRoot.ChangeEventDetails) → void- Parameters
value– Value | nulleventDetails– NavigationMenuRoot.ChangeEventDetails- Returns
void
actionsRef?React.RefObject<NavigationMenuRoot.Actions | null>
A ref to imperative actions.
- unmount: Ends the closing phase of the navigation menu popup after an externally controlled closing animation finishes.
Call preventUnmountOnClose() in onValueChange first, otherwise the navigation menu popup completes closing on its own.
Whether it leaves the DOM is decided by keepMounted on the portal.
- close: Closes the navigation menu imperatively when called.
- Type
React.RefObject<NavigationMenuRoot.Actions | null>
closeDelay?number
How long to wait before closing the navigation popup. Specified in milliseconds.
- Default
50
delay?number
How long to wait before opening the navigation popup. Specified in milliseconds.
- Default
50
onOpenChangeComplete?(open: boolean) → void
Event handler called after any animations complete when the navigation menu is closed.
- Parameters
open– boolean- Returns
void
orientation?"horizontal" | "vertical"
The orientation of the navigation menu.
- Type
"horizontal" | "vertical"- Default
"horizontal"
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string
Merged with the part's classes.
data-orientationstring
Mirrors the orientation prop.
--trigger-heightstring--trigger-widthstring--trigger-xstring--trigger-ystring--viewport-heightstring--viewport-widthstring--viewport-xstring--viewport-ystringNavigationMenuViewport
<div>
--available-widthstring
The width left between the anchor and the viewport's edge.
--popup-heightstring--popup-widthstring--positioner-heightstring--positioner-widthstring--transform-originstring
Where the content scales from, on its trigger's side.
NavigationMenuTrigger
<button> data-slot="navigation-menu-trigger"
disabled?boolean
Whether the component should ignore user interaction.
- Default
false
nativeButton?boolean
Whether the component renders a native <button> element when replacing it
via the render prop.
Set to false if the rendered element is not a button (for example, <div>).
- Default
true
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string
Merged with the part's classes.
data-disabled?string
Present when disabled is true.
data-state"open" | "closed"
"open" or "closed".
- Type
"open" | "closed"
data-trigger-proxy-idstring
Id of the triggerProxy part.
data-uidstring
Mirrors the id prop.
data-valuestring
The item's value.
NavigationMenuContent
<div> data-slot="navigation-menu-content"
keepMounted?boolean
Whether to keep the content mounted in the DOM while the popup is closed. Ensures the content is present during server-side rendering for web crawlers.
- Default
false
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string
Merged with the part's classes.
data-orientationstring
Mirrors the orientation prop.
data-state"open" | "closed"
"open" or "closed".
- Type
"open" | "closed"
data-uidstring
Mirrors the id prop.
data-valuestring
The item's value.
NavigationMenuLink
<a> data-slot="navigation-menu-link"
active?boolean
Whether the link is the currently active page.
- Default
false
closeOnClick?boolean
Whether to close the navigation menu when the link is clicked.
- Default
false
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string
Merged with the part's classes.
data-current?string
Present when current is true.
data-ownedbystring
Id of the content part.
data-valuestring
The item's value.
NavigationMenuList
data-slot="navigation-menu-list"
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string
Merged with the part's classes.
data-orientationstring
Mirrors the orientation prop.
NavigationMenuItem
data-slot="navigation-menu-item"
value?any
A unique value that identifies this navigation menu item. If no value is provided, a unique ID will be generated automatically. Use when controlling the navigation menu programmatically.
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string
Merged with the part's classes.
data-orientationstring
Mirrors the orientation prop.
NavigationMenuPositioner
<div>
align?"start" | "center" | "end"
How to align the popup relative to the specified side. Ark default: center.
- Type
"start" | "center" | "end"- Default
"start"
alignOffset?number | OffsetFunction
Additional offset along the alignment axis in pixels.
Also accepts a function that returns the offset to read the dimensions of the anchor
and positioner elements, along with its side and alignment.
The function takes a data object parameter with the following properties:
- data.anchor: the dimensions of the anchor element with properties width and height.
- data.positioner: the dimensions of the positioner element with properties width and height.
- data.side: which side of the anchor element the positioner is aligned against.
- data.align: how the positioner is aligned relative to the specified side.
``jsx
<Positioner
alignOffset={({ side, align, anchor, positioner }) => {
return side === top || side === bottom
? anchor.width
: anchor.height;
}}
/>
``
- Type
number | OffsetFunction- Default
0
anchor?Element | null | VirtualElement | React.RefObject<Element | null> | (() → Element | VirtualElement | null)
An element to position the popup against. By default, the popup will be positioned against the trigger.
- Type
Element | null | VirtualElement | React.RefObject<Element | null> | (() → Element | VirtualElement | null)
arrowPadding?number
Minimum distance to maintain between the arrow and the edges of the popup. Use it to prevent the arrow element from hanging out of the rounded corners of a popup.
- Default
5
collisionAvoidance?SideFlipMode | SideShiftMode
Determines how to handle collisions when positioning the popup.
side controls overflow on the preferred placement axis (top/bottom or left/right):
- flip: keep the requested side when it fits; otherwise try the opposite side
(top and bottom, or left and right).
- shift: never change side; keep the requested side and move the popup within
the clipping boundary so it stays visible.
- none: do not correct side-axis overflow.
align controls overflow on the alignment axis (start/center/end):
- flip: keep side, but swap start and end when the requested alignment overflows.
- shift: keep side and requested alignment, then nudge the popup along the
alignment axis to fit.
- none: do not correct alignment-axis overflow.
fallbackAxisSide controls fallback behavior on the perpendicular axis when the
preferred axis cannot fit:
- start: allow perpendicular fallback and try the logical start side first
(top before bottom, or left before right in LTR).
- end: allow perpendicular fallback and try the logical end side first
(bottom before top, or right before left in LTR).
- none: do not fallback to the perpendicular axis.
When side is shift, explicitly setting align only supports shift or none.
If align is omitted, it defaults to flip.
``jsx
<Positioner
collisionAvoidance={{
side: shift,
align: shift,
fallbackAxisSide: none,
}}
/>
``
- Type
SideFlipMode | SideShiftMode
collisionBoundary?"clipping-ancestors" | Element | Element[] | Rect
An element or a rectangle that delimits the area that the popup is confined to.
- Type
"clipping-ancestors" | Element | Element[] | Rect- Default
"clipping-ancestors"
collisionPadding?Padding
Additional space to maintain from the edge of the collision boundary.
- Default
5
disableAnchorTracking?boolean
Whether to disable the popup from tracking any layout shift of its positioning anchor.
- Default
false
positionMethod?"absolute" | "fixed"
Determines which CSS position property to use.
- Type
"absolute" | "fixed"- Default
"absolute"
side?"top" | "bottom" | "left" | "right" | "inline-end" | "inline-start"
Which side of the anchor element to align the popup against. May automatically change to avoid collisions.
- Type
"top" | "bottom" | "left" | "right" | "inline-end" | "inline-start"- Default
"bottom"
sideOffset?number | OffsetFunction
Distance between the anchor and the popup in pixels.
Also accepts a function that returns the distance to read the dimensions of the anchor
and positioner elements, along with its side and alignment.
The function takes a data object parameter with the following properties:
- data.anchor: the dimensions of the anchor element with properties width and height.
- data.positioner: the dimensions of the positioner element with properties width and height.
- data.side: which side of the anchor element the positioner is aligned against.
- data.align: how the positioner is aligned relative to the specified side.
``jsx
<Positioner
sideOffset={({ side, align, anchor, positioner }) => {
return side === top || side === bottom
? anchor.height
: anchor.width;
}}
/>
`
Ark default: 0`.
- Type
number | OffsetFunction- Default
8
sticky?boolean
Whether to maintain the popup in the viewport after the anchor element was scrolled out of view.
- Default
false
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string
Merged with the part's classes.
--available-widthstring
The width left between the anchor and the viewport's edge.
--popup-heightstring--popup-widthstring--positioner-heightstring--positioner-widthstring--transform-originstring
Where the content scales from, on its trigger's side.
NavigationMenuIndicator
data-slot="navigation-menu-indicator"
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string
Merged with the part's classes.
data-orientationstring
Mirrors the orientation prop.
With other components
- Free
Button
ButtonsTrigger looks like a button
- Free
Sheet
OverlaysSame links on phones
- Free
Badge
FeedbackNew next to a link
- Free
Card
LayoutFeatured cell in mega menu
- Freeor
Separator
LayoutSplit groups in a panel
- Free//
Breadcrumb
NavigationTrail under the header
Related
- Free▼
Dropdown Menu
MenusActions, not site nav
- Free
Menubar
MenusFile, Edit, View
- Free
Sidebar
NavigationPersistent app chrome
- Free
Hover Card
OverlaysPreview behind a link
- Free
Tabs
NavigationSame-page sections
- Free
Bottom Navigation
NavigationFixed to the bottom edge