A collection of React components for building accessible, customizable navigation menus with keyboard support and flexible layout options
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Indicator component for visual feedback and sub-menu support for nested navigation structures.
Visual indicator component that shows the active menu item with automatic positioning and animation support.
/**
* Visual indicator that highlights the active menu item
* Automatically positions itself relative to the active trigger
* @param forceMount - Force mounting for animation control
*/
const NavigationMenuIndicator: React.ForwardRefExoticComponent<
NavigationMenuIndicatorProps & React.RefAttributes<HTMLDivElement>
>;
interface NavigationMenuIndicatorProps
extends React.ComponentPropsWithoutRef<"div"> {
forceMount?: true;
}Key Features:
forceMountUsage Examples:
import {
NavigationMenu,
NavigationMenuList,
NavigationMenuItem,
NavigationMenuTrigger,
NavigationMenuContent,
NavigationMenuIndicator
} from "@radix-ui/react-navigation-menu";
// Basic indicator usage
function IndicatorExample() {
return (
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Home</NavigationMenuTrigger>
<NavigationMenuContent>Home content</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Products</NavigationMenuTrigger>
<NavigationMenuContent>Products content</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Services</NavigationMenuTrigger>
<NavigationMenuContent>Services content</NavigationMenuContent>
</NavigationMenuItem>
{/* Indicator automatically positions over active trigger */}
<NavigationMenuIndicator className="nav-indicator" />
</NavigationMenuList>
</NavigationMenu>
);
}
// Styled indicator with animations
function StyledIndicatorExample() {
return (
<NavigationMenu>
<NavigationMenuList className="nav-list">
<NavigationMenuItem>
<NavigationMenuTrigger>Tab 1</NavigationMenuTrigger>
<NavigationMenuContent>Content 1</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Tab 2</NavigationMenuTrigger>
<NavigationMenuContent>Content 2</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuIndicator
className="indicator"
style={{
bottom: 0,
height: "2px",
backgroundColor: "blue",
transition: "transform 200ms ease",
}}
/>
</NavigationMenuList>
</NavigationMenu>
);
}
// Force mounted indicator for custom animations
function AnimatedIndicatorExample() {
return (
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Item 1</NavigationMenuTrigger>
<NavigationMenuContent>Content 1</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Item 2</NavigationMenuTrigger>
<NavigationMenuContent>Content 2</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuIndicator
forceMount
className="custom-animated-indicator"
/>
</NavigationMenuList>
</NavigationMenu>
);
}function VerticalIndicatorExample() {
return (
<NavigationMenu orientation="vertical">
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Vertical Item 1</NavigationMenuTrigger>
<NavigationMenuContent>Content 1</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Vertical Item 2</NavigationMenuTrigger>
<NavigationMenuContent>Content 2</NavigationMenuContent>
</NavigationMenuItem>
{/* Indicator adapts to vertical orientation */}
<NavigationMenuIndicator
style={{
left: 0,
width: "3px",
backgroundColor: "green",
transition: "transform 200ms ease",
}}
/>
</NavigationMenuList>
</NavigationMenu>
);
}Sub-menus inherit from the same core architecture but operate within a nested context:
import {
NavigationMenu,
NavigationMenuList,
NavigationMenuItem,
NavigationMenuTrigger,
NavigationMenuContent,
NavigationMenuSub
} from "@radix-ui/react-navigation-menu";
function SubMenuExample() {
return (
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Main Category</NavigationMenuTrigger>
<NavigationMenuContent>
{/* Sub-menu within content */}
<NavigationMenuSub>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Subcategory 1</NavigationMenuTrigger>
<NavigationMenuContent>
Sub-content 1
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Subcategory 2</NavigationMenuTrigger>
<NavigationMenuContent>
Sub-content 2
</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenuSub>
</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
);
}function ControlledSubMenuExample() {
const [subValue, setSubValue] = React.useState("");
return (
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Products</NavigationMenuTrigger>
<NavigationMenuContent>
<NavigationMenuSub
value={subValue}
onValueChange={setSubValue}
orientation="vertical"
>
<NavigationMenuList>
<NavigationMenuItem value="web">
<NavigationMenuTrigger>Web Products</NavigationMenuTrigger>
<NavigationMenuContent>
Web product details
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem value="mobile">
<NavigationMenuTrigger>Mobile Products</NavigationMenuTrigger>
<NavigationMenuContent>
Mobile product details
</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenuSub>
</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
);
}function ComplexNestedExample() {
return (
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Solutions</NavigationMenuTrigger>
<NavigationMenuContent>
<div className="mega-menu">
<div className="menu-column">
<h3>By Industry</h3>
<NavigationMenuSub>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Healthcare</NavigationMenuTrigger>
<NavigationMenuContent>
<NavigationMenuLink href="/healthcare/hospitals">Hospitals</NavigationMenuLink>
<NavigationMenuLink href="/healthcare/clinics">Clinics</NavigationMenuLink>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Finance</NavigationMenuTrigger>
<NavigationMenuContent>
<NavigationMenuLink href="/finance/banking">Banking</NavigationMenuLink>
<NavigationMenuLink href="/finance/insurance">Insurance</NavigationMenuLink>
</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenuSub>
</div>
<div className="menu-column">
<h3>By Size</h3>
<NavigationMenuSub>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuLink href="/enterprise">Enterprise</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink href="/small-business">Small Business</NavigationMenuLink>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenuSub>
</div>
</div>
</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
);
}The indicator automatically handles positioning based on the active trigger:
transform: translateX() for positioningtransform: translateY() for positioning/* Horizontal indicator */
.nav-indicator[data-orientation="horizontal"] {
bottom: 0;
height: 2px;
background: var(--accent-color);
transition: transform 200ms ease;
}
/* Vertical indicator */
.nav-indicator[data-orientation="vertical"] {
left: 0;
width: 3px;
background: var(--accent-color);
transition: transform 200ms ease;
}
/* Indicator states */
.nav-indicator[data-state="visible"] {
opacity: 1;
}
.nav-indicator[data-state="hidden"] {
opacity: 0;
}Both indicators and sub-menus expose data attributes for styling:
[data-state="visible"] { /* Indicator is shown */ }
[data-state="hidden"] { /* Indicator is hidden */ }
[data-orientation="horizontal"] { /* Horizontal layout */ }
[data-orientation="vertical"] { /* Vertical layout */ }Advanced Indicator Styling:
.nav-indicator {
position: absolute;
transition: all 200ms ease;
}
.nav-indicator[data-state="visible"] {
opacity: 1;
transform: scale(1);
}
.nav-indicator[data-state="hidden"] {
opacity: 0;
transform: scale(0.8);
}
.nav-indicator[data-orientation="horizontal"] {
width: 100%;
height: 2px;
bottom: 0;
left: 0;
}
.nav-indicator[data-orientation="vertical"] {
width: 3px;
height: 100%;
left: 0;
top: 0;
}[data-orientation="horizontal"] { /* Sub-menu horizontal layout */ }
[data-orientation="vertical"] { /* Sub-menu vertical layout */ }Sub-menu Styling:
.nav-sub[data-orientation="horizontal"] {
display: flex;
flex-direction: row;
}
.nav-sub[data-orientation="vertical"] {
display: flex;
flex-direction: column;
}These attributes enable CSS-based styling and animations without JavaScript.
Install with Tessl CLI
npx tessl i tessl/npm-radix-ui--react-navigation-menu