A collection of links for navigating websites.
Can be controlled or uncontrolled.
Flexible layout structure with managed tab focus.
Supports submenus.
Optional active item indicator.
Full keyboard navigation.
Exposes CSS variables for advanced animation.
Supports custom timings.
Install the component from your command line.
npm install @radix-ui/react-navigation-menu
Import all parts and piece them together.
import * as NavigationMenu from '@radix-ui/react-navigation-menu';
export default () => (
<NavigationMenu.Root>
<NavigationMenu.List>
<NavigationMenu.Item>
<NavigationMenu.Trigger />
<NavigationMenu.Content>
<NavigationMenu.Link />
</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Link />
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Trigger />
<NavigationMenu.Content>
<NavigationMenu.Sub>
<NavigationMenu.List />
<NavigationMenu.Viewport />
</NavigationMenu.Sub>
</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Indicator />
</NavigationMenu.List>
<NavigationMenu.Viewport />
</NavigationMenu.Root>
);
Contains all the parts of a navigation menu.
Signifies a submenu. Use it in place of the root part when nested to create a submenu.
Contains the top level menu items.
A top level menu item, contains a link or trigger with content combination.
The button that toggles the content.
Contains the content associated with each trigger.
A navigational link.
An optional indicator element that renders below the list, is used to highlight the currently active trigger.
An optional viewport element that is used to render active content outside of the list.
You can create a vertical menu by using the orientation
prop.
<NavigationMenu.Root orientation="vertical">
<NavigationMenu.List>
<NavigationMenu.Item>
<NavigationMenu.Trigger>Item one</NavigationMenu.Trigger>
<NavigationMenu.Content>Item one content</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Trigger>Item two</NavigationMenu.Trigger>
<NavigationMenu.Content>Item Two content</NavigationMenu.Content>
</NavigationMenu.Item>
</NavigationMenu.List>
</NavigationMenu.Root>
Use the Viewport
part when you need extra control over where Content
is rendered. This can be helpful when your design
requires an adjusted DOM structure or if you need flexibility to achieve advanced animation.
Tab focus will be maintained automatically.
<NavigationMenu.Root>
<NavigationMenu.List>
<NavigationMenu.Item>
<NavigationMenu.Trigger>Item one</NavigationMenu.Trigger>
<NavigationMenu.Content>Item one content</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Trigger>Item two</NavigationMenu.Trigger>
<NavigationMenu.Content>Item two content</NavigationMenu.Content>
</NavigationMenu.Item>
</NavigationMenu.List>
{/* NavigationMenu.Content will be rendered here when active */}
<NavigationMenu.Viewport />
</NavigationMenu.Root>
You can use the optional Indicator
part to highlight the currently active Trigger
, this is useful when you want to provide
an animated visual cue such as an arrow or highlight to accompany the Viewport
.
// index.jsx
import * as NavigationMenu from '@radix-ui/react-navigation-menu';
import './styles.css';
export default () => (
<NavigationMenu.Root>
<NavigationMenu.List>
<NavigationMenu.Item>
<NavigationMenu.Trigger>Item one</NavigationMenu.Trigger>
<NavigationMenu.Content>Item one content</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Trigger>Item two</NavigationMenu.Trigger>
<NavigationMenu.Content>Item two content</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Indicator className="NavigationMenuIndicator" />
</NavigationMenu.List>
<NavigationMenu.Viewport />
</NavigationMenu.Root>
);
/* styles.css */
.NavigationMenuIndicator {
background-color: grey;
}
.NavigationMenuIndicator[data-orientation='horizontal'] {
height: 3px;
transition: width, transform, 250ms ease;
}
Create a submenu by nesting your NavigationMenu
and using the Sub
part in place of its Root
.
Submenus work differently to Root
navigation menus and are similar to Tabs
in that one item should always be active, so be
sure to assign and set a defaultValue
.
<NavigationMenu.Root>
<NavigationMenu.List>
<NavigationMenu.Item>
<NavigationMenu.Trigger>Item one</NavigationMenu.Trigger>
<NavigationMenu.Content>Item one content</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Trigger>Item two</NavigationMenu.Trigger>
<NavigationMenu.Content>
<NavigationMenu.Sub defaultValue="sub1">
<NavigationMenu.List>
<NavigationMenu.Item value="sub1">
<NavigationMenu.Trigger>Sub item one</NavigationMenu.Trigger>
<NavigationMenu.Content>
Sub item one content
</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item value="sub2">
<NavigationMenu.Trigger>Sub item two</NavigationMenu.Trigger>
<NavigationMenu.Content>
Sub item two content
</NavigationMenu.Content>
</NavigationMenu.Item>
</NavigationMenu.List>
</NavigationMenu.Sub>
</NavigationMenu.Content>
</NavigationMenu.Item>
</NavigationMenu.List>
</NavigationMenu.Root>
If you need to use the Link
component provided by your routing package then we recommend composing with NavigationMenu.Link
via a custom component.
This will ensure accessibility and consistent keyboard control is maintained. Here's an example using Next.js:
// index.jsx
import { useRouter } from 'next/router';
import NextLink from 'next/link';
import * as NavigationMenu from '@radix-ui/react-navigation-menu';
import './styles.css';
const Link = ({ href, ...props }) => {
const router = useRouter();
const isActive = router.asPath === href;
return (
<NextLink href={href} passHref legacyBehavior>
<NavigationMenu.Link
className="NavigationMenuLink"
active={isActive}
{...props}
/>
</NextLink>
);
};
export default () => (
<NavigationMenu.Root>
<NavigationMenu.List>
<NavigationMenu.Item>
<Link href="/">Home</Link>
</NavigationMenu.Item>
<NavigationMenu.Item>
<Link href="/about">About</Link>
</NavigationMenu.Item>
</NavigationMenu.List>
</NavigationMenu.Root>
);
/* styles.css */
.NavigationMenuLink {
text-decoration: none;
}
.NavigationMenuLink[data-active] {
text-decoration: 'underline';
}
We expose --radix-navigation-menu-viewport-[width|height]
and data-motion['from-start'|'to-start'|'from-end'|'to-end']
attributes
to allow you to animate Viewport
size and Content
position based on the enter/exit direction.
Combining these with position: absolute;
allows you to create smooth overlapping animation effects when moving between items.
// index.jsx
import * as NavigationMenu from '@radix-ui/react-navigation-menu';
import './styles.css';
export default () => (
<NavigationMenu.Root>
<NavigationMenu.List>
<NavigationMenu.Item>
<NavigationMenu.Trigger>Item one</NavigationMenu.Trigger>
<NavigationMenu.Content className="NavigationMenuContent">
Item one content
</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Trigger>Item two</NavigationMenu.Trigger>
<NavigationMenu.Content className="NavigationMenuContent">
Item two content
</NavigationMenu.Content>
</NavigationMenu.Item>
</NavigationMenu.List>
<NavigationMenu.Viewport className="NavigationMenuViewport" />
</NavigationMenu.Root>
);
/* styles.css */
.NavigationMenuContent {
position: absolute;
top: 0;
left: 0;
animation-duration: 250ms;
animation-timing-function: ease;
}
.NavigationMenuContent[data-motion='from-start'] {
animation-name: enterFromLeft;
}
.NavigationMenuContent[data-motion='from-end'] {
animation-name: enterFromRight;
}
.NavigationMenuContent[data-motion='to-start'] {
animation-name: exitToLeft;
}
.NavigationMenuContent[data-motion='to-end'] {
animation-name: exitToRight;
}
.NavigationMenuViewport {
position: relative;
width: var(--radix-navigation-menu-viewport-width);
height: var(--radix-navigation-menu-viewport-height);
transition: width, height, 250ms ease;
}
@keyframes enterFromRight {
from {
opacity: 0;
transform: translateX(200px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes enterFromLeft {
from {
opacity: 0;
transform: translateX(-200px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes exitToRight {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(200px);
}
}
@keyframes exitToLeft {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(-200px);
}
}
Adheres to the navigation
role requirements.
NavigationMenu
should not be confused with menubar
, although this primitive shares the name menu
in the colloquial sense to refer to a set of navigation links, it does not use the WAI-ARIA menu
role.
This is because menu
and menubars
behave like native operating system menus most commonly found in desktop application windows, as such they feature complex functionality like composite focus management and first-character navigation.
These features are often considered unnecessary for website navigation and at worst can confuse users who are familiar with established website patterns.
See the W3C Disclosure Navigation Menu example for more information.
It's important to use NavigationMenu.Link
for all navigational links within a menu, this not only applies to the main list
but also within any content rendered via NavigationMenu.Content
. This will ensure consistent keyboard interactions and accessibility
while also giving access to the active
prop for setting aria-current
and the active styles.
See this example for more information on usage with third party routing components.