A background that glides between items on click, on hover or with a controlled value.
Installation
Usage
import { Highlight } from '@animate-ui/react/primitives/effects/highlight';
export function Segmented() {
return (
<Highlight defaultValue="overview" className="rounded-full bg-accent">
<button data-value="overview">Overview</button>
<button data-value="activity">Activity</button>
<button data-value="settings">Settings</button>
</Highlight>
);
}Each child is wrapped in a HighlightItem. Its value comes from id, value, data-value or the child's id, in that order. Items receive data-active="true" when highlighted, for styling.
Controlled items
With controlledItems, place HighlightItems anywhere inside Highlight instead of as direct children.
<Highlight controlledItems hover className="rounded-md bg-accent">
<nav>
<HighlightItem value="docs">
<a href="/docs">Docs</a>
</HighlightItem>
<HighlightItem value="blog">
<a href="/blog">Blog</a>
</HighlightItem>
</nav>
</Highlight>Modes
children(default): the highlight is rendered inside the active item and moves with a shared layout animation. Prefer it in most cases.parent: the highlight is rendered once in a container, and its position and size spring to the active item. Use it when the items clip or stack in a way that hides thechildrenhighlight.
<Highlight mode="parent" containerClassName="flex" className="bg-accent">
<div data-value="a">A</div>
<div data-value="b">B</div>
</Highlight>Animation
- In
childrenmode, the highlight slides to the new item on a spring (stiffness 500, damping 40) with a shared layout animation: the previous item hands its surface over in the same frame, so one surface slides at full opacity. It fades in when nothing was highlighted, and fades out afterexitDelay(200ms) when no item takes it. - In
parentmode, a single highlight springs its top, left, width and height to the active item, fades in when it appears, and fades outexitDelayafter the last item is left. - Leaving with
hoverfades it out afterexitDelay, so moving between items doesn't flicker. hoveronly applies to pointers that can hover (mouse, trackpad): touch taps never leave a highlight stuck.- In
parentmode,forceUpdateBoundsmeasures the active item on every frame, so the highlight follows it whatever moves it (scrolling, resizing, an animated container). <AnimateUIProvider tempo>paces the transition andexitDelay.- Under reduced motion, the highlight jumps to the new item and only fades.
API Reference
Highlight
| Prop | Type | Default |
|---|---|---|
as? | React.ElementType | 'div' |
mode? | 'children' | 'parent' | 'children' |
value? | string | null | - |
defaultValue? | string | null | - |
onValueChange? | (value: string | null) => void | - |
className? | string | - |
style? | React.CSSProperties | - |
transition? | Transition | { type: 'spring', stiffness: 500, damping: 40 } |
hover? | boolean | false |
click? | boolean | true |
disabled? | boolean | false |
enabled? | boolean | true |
exitDelay? | number | 200 |
controlledItems? | boolean | - |
itemsClassName? | string | - |
Parent mode
| Prop | Type | Default |
|---|---|---|
boundsOffset? | Partial<{ top: number; left: number; width: number; height: number }> | - |
containerClassName? | string | - |
forceUpdateBounds? | boolean | - |
HighlightItem
| Prop | Type | Default |
|---|---|---|
as? | React.ElementType | 'div' |
children | React.ReactElement | - |
id? | string | - |
value? | string | - |
className? | string | - |
activeClassName? | string | - |
style? | React.CSSProperties | - |
transition? | Transition | - |
disabled? | boolean | - |
exitDelay? | number | - |
asChild? | boolean | false |
forceUpdateBounds? | boolean | - |
useHighlight
Returns the state of the closest Highlight: activeValue, setActiveValue, mode, and its options.