A shared Motion-powered highlight that slides between items on hover, or fades per-item on click. React component.
Made by AxylA shared pill highlight slides between nav items on hover — move slowly to see the spring.
A per-item fade background activated on click, useful for menus and sidebars.
One motion.div lives inside the container and slides to whichever item is active. This is the same pattern used by the sidebar hover highlight.
Each item renders its own motion.div overlay carrying one shared layoutId, so the background slides from the previous item to the new one as you move or click. Exactly one overlay is mounted at a time and it never fades, which keeps the colour identical the whole way across — crossfading two translucent layers never adds back up to a single layer.
| Prop | Type | Default |
|---|---|---|
mode? | "parent" | "children" | "children" |
trigger? | "hover" | "click" | "click" |
value? | string | null | - |
defaultValue? | string | null | - |
onValueChange? | (value: string | null) => void | - |
className? | string | - |
style? | CSSProperties | - |
transition? | Transition | { type: "spring", stiffness: 350, damping: 35 } |
exitDelay? | number | 0 |
disabled? | boolean | false |
enabled? | boolean | true |
itemsClassName? | string | - |
containerClassName? | string | - |
boundsOffset? | Partial<{ top: number; left: number; width: number; height: number }> | - |
className styles the generated wrapper div — this is where layout classes like
flex-1 or h-full belong. Use activeClassName to style the highlight overlay.
| Prop | Type | Default |
|---|---|---|
value? | string | - |
disabled? | boolean | - |
activeClassName? | string | - |
className? | string | - |
style? | CSSProperties | - |
transition? | Transition | - |
asChild? | boolean | false |
HighlightItem never takes over your markup. With asChild it merges into the
child element while preserving what the child already owns:
ref is composed, not replaced — Radix or cmdk items keep measuring themselves.onClick / onMouseEnter / onMouseLeave still run, then the highlight activates.aria-selected, data-active) win over the injected ones.Both components forward ref and accept native div props. On Highlight the
forwarded ref points at the container element in mode="parent", and is null in
mode="children" where no container is rendered.
Controlled means controlled
Passing value puts Highlight fully in your hands: clicks and hovers only
report through onValueChange, the highlight never moves on its own. Forget to
wire the callback and nothing will highlight.
defaultValue seeds the active item and the component owns it from then on.
Children are never double-wrapped
Writing your own <HighlightItem> children needs no extra flag — Highlight
detects them and leaves them alone. Any other element is wrapped for you.
Use mode="parent" for navigation bars, tab rows, and any horizontal/vertical list where you want one smooth background element sliding between items. The spring physics make the motion feel natural even when the cursor skips quickly across items.
Use mode="children" for menus, sidebars, and selection lists where each item independently slides its own background into place.
Avoid using trigger="hover" with mode="children" and widely-spaced items — the per-item fade-in/out can feel noisy. In that case, prefer mode="parent" which slides a single element.
HighlightItem sets aria-selected and data-active on each item wrapper. The highlight overlay itself is decorative (pointer-events-none) and invisible to assistive technologies. Keyboard navigation is not wired automatically — connect it via the controlled value/onValueChange API if needed.
aria-selected is only meaningful on an element that carries the matching role. If you build a listbox or tablist, pass role (and any other ARIA attributes) to HighlightItem; they land on the wrapper the attributes describe.
In mode="children" there is no container element, so native div props on Highlight (role, onKeyDown, id…) are not rendered anywhere. Use mode="parent", or enabled={false} to opt out of the highlight entirely. In development this logs a warning naming the ignored props.
When prefers-reduced-motion: reduce is set, useReducedMotion() collapses the transition to duration: 0 — the highlight still moves to the active item's bounds, just instantly instead of animating.
Inline Testimonials
A flowing quote stream where each avatar reveals its author on hover, with Motion fading and blurring the rest. React component.
Progressive Blur
A stacked-layer blur that ramps smoothly from sharp to fully blurred toward an edge, for fading content under a fixed header or above a scroll boundary. React.
Built by Axyl. A motion-first component registry for React.
Last updated: 10/15/2026
Help keep SoraLabs moving.
Every contribution helps us build and maintain open-source tools for the community.
Sponsor on GitHub