Tabs
Code
| 1 | // Measure the active tab's offset + width so the underline hugs each |
| 2 | // label exactly (no fixed-width box, no hardcoded offset math). |
| 3 | const [indicator, setIndicator] = useState({ x: 0, w: 0 }); |
| 4 | |
| 5 | useLayoutEffect(() => { |
| 6 | const el = tabRefs.current[active]; |
| 7 | if (el) setIndicator({ x: el.offsetLeft, w: el.offsetWidth }); |
| 8 | }, [active]); |
| 9 | |
| 10 | // <Anime> slides AND resizes the underline to the active tab. |
| 11 | // `autoplay` is required: hooks default to autoplay={false}, so deps |
| 12 | // alone would recreate the tween without ever playing it. |
| 13 | <Anime |
| 14 | translateX={indicator.x} |
| 15 | width={indicator.w} |
| 16 | duration={300} |
| 17 | ease="outExpo" |
| 18 | autoplay |
| 19 | deps={[indicator.x, indicator.w]} |
| 20 | > |
| 21 | <span className="underline" /> |
| 22 | </Anime> |
| 23 | |
| 24 | // Active panel mounts/unmounts as `active` changes. <AnimePresence> |
| 25 | // drives the cross-fade; mode="wait" gives a clean out-then-in swap. |
| 26 | <AnimePresence mode="wait" initial={false}> |
| 27 | <AnimePresenceChild |
| 28 | key={panels[active].label} |
| 29 | enter={{ opacity: [0, 1], translateY: [6, 0] }} |
| 30 | exit={{ opacity: [1, 0], translateY: [0, -6] }} |
| 31 | duration={220} |
| 32 | ease="outQuad" |
| 33 | > |
| 34 | <div className="panel">{panels[active].body}</div> |
| 35 | </AnimePresenceChild> |
| 36 | </AnimePresence> |
Animated underline indicator with cross-fading content panels.
Live preview
TabsClick a tab to switch
A hooks-first React wrapper for anime.js v4. Declarative components where they help.
Props
| Name | Type | Default | Description |
|---|---|---|---|
| translateX | number | - | Indicator slide to active tab offset |
| width | number | - | Indicator width matching active tab |
| autoplay | boolean | false | Plays the (re)created tween — required with deps |
| deps | unknown[] | [] | Re-runs the animation when values change |
| mode | 'wait' | 'wait' | Exit completes before next panel enters |
| enter | UseAnimeOptions | - | Panel enter keyframes |
| exit | UseAnimeOptions | - | Panel exit keyframes |
| duration | number | 220 | Transition ms |