Motion Navigation Menu
A spring-animated navigation menu with a single morphing container, layout-animated active pill, and direction-aware content transitions.
Installation
File Structure
highlight.tsx
motion-navigation-menu.tsx
Usage
import {
MotionNavigationMenu,
MotionNavigationMenuContent,
MotionNavigationMenuIndicator,
MotionNavigationMenuItem,
MotionNavigationMenuLink,
MotionNavigationMenuList,
MotionNavigationMenuTrigger,
} from "@/components/unlumen-ui/motion-navigation-menu";
const listHighlightClassName = "bg-primary/10 rounded-lg";
const contentHighlightClassName =
"bg-primary/10 rounded-lg ring-1 ring-primary/15";
export default function Example() {
return (
<MotionNavigationMenu
viewportClassName="bg-surface border-none shadow-none"
springStiffness={350}
springDamping={32}
>
<MotionNavigationMenuList highlightClassName={listHighlightClassName}>
<MotionNavigationMenuItem value="products">
<MotionNavigationMenuTrigger>Products</MotionNavigationMenuTrigger>
<MotionNavigationMenuContent
highlightClassName={contentHighlightClassName}
>
<div className="grid w-[400px] grid-cols-2 gap-1">
<MotionNavigationMenuLink href="/analytics">
<span className="text-sm font-medium">Analytics</span>
<span className="text-muted-foreground text-xs">
Understand your data flow.
</span>
</MotionNavigationMenuLink>
<MotionNavigationMenuLink href="/automation">
<span className="text-sm font-medium">Automation</span>
<span className="text-muted-foreground text-xs">
Streamline workflows.
</span>
</MotionNavigationMenuLink>
</div>
</MotionNavigationMenuContent>
</MotionNavigationMenuItem>
<MotionNavigationMenuIndicator />
<MotionNavigationMenuItem>
<MotionNavigationMenuLink
href="/pricing"
className="px-4 py-2 text-sm font-medium"
>
Pricing
</MotionNavigationMenuLink>
</MotionNavigationMenuItem>
</MotionNavigationMenuList>
</MotionNavigationMenu>
);
}API Reference
MotionNavigationMenu
| Prop | Type | Default | Description |
|---|---|---|---|
viewport? | boolean | true | When true, renders content inside the shared morphing viewport. When false, each content panel renders absolutely below its item. |
viewportClassName? | string | - | Additional classes applied to the morphing viewport. |
springBounce? | number | 0 | Bounce used by the spring transition for the viewport and content animations. |
springStiffness? | number | 350 | Stiffness used by the spring transition for the viewport and content animations. |
springDamping? | number | 32 | Damping used by the spring transition for the viewport and content animations. |
value? | string | - | Controlled active item value. Use an empty string to close the menu. |
onValueChange? | (value: string) => void | - | Callback fired when the active item value changes. |
className? | string | - | Additional classes applied to the root element. |
MotionNavigationMenuList
| Prop | Type | Default | Description |
|---|---|---|---|
className? | string | - | Additional classes applied to the list element. |
highlightClassName? | string | - | Additional classes applied to the hover highlight rendered behind trigger items. |
MotionNavigationMenuItem
| Prop | Type | Default | Description |
|---|---|---|---|
value? | string | - | Unique identifier for this item. Required when the item has a Trigger + Content pair. |
className? | string | - | Additional classes applied to the list item. |
MotionNavigationMenuTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
className? | string | - | Additional classes applied to the trigger button. |
children? | React.ReactNode | - | Trigger content. |
MotionNavigationMenuContent
Renders the panel shown when the parent item's trigger is active. Must be placed inside a MotionNavigationMenuItem that has a value prop. The content is portal-rendered into the morphing container, so its width drives the container's resize animation.
| Prop | Type | Default | Description |
|---|---|---|---|
className? | string | - | Additional classes applied to the content wrapper. |
highlightClassName? | string | - | Additional classes applied to the hover highlight rendered behind links inside the content. |
innerClassName? | string | - | Additional classes applied to the inner content container. |
MotionNavigationMenuLink
| Prop | Type | Default | Description |
|---|---|---|---|
href? | string | - | Destination URL for the anchor. |
className? | string | - | Additional classes applied to the link. |
children? | React.ReactNode | - | Link content. |
MotionNavigationMenuIndicator
| Prop | Type | Default | Description |
|---|---|---|---|
className? | string | - | Additional classes applied to the indicator wrapper. |
Notes
- Shared viewport: with
viewportenabled, open content is rendered inside one morphing viewport that animates width, height, opacity, and scale. - Direction-aware transitions: switching between menus slides content horizontally based on whether the new item is to the left or right of the previous one.
- Custom highlights: use
MotionNavigationMenuList'shighlightClassNamefor trigger hover styling andMotionNavigationMenuContent'shighlightClassNamefor dropdown link hover styling. - Controlled mode: pass
valueandonValueChangeto control the active item from outside the component. - Overflow clamping: the dropdown automatically stays within its nearest clipping ancestor — no configuration needed when the menu is inside a constrained container like a preview panel or modal.
Credits
Built by Léo.
Keep in mind
Most components on this site are inspired by or recreated from existing work across the web. I'm not here to take credit; just to learn, experiment, and sometimes push things a bit further. If something looks familiar and I forgot to mention you, reach out and I'll fix that right away.