use-animation-sequence
v0.1.0
Published
A React hook for orchestrating multi-step CSS class animations. Declare frame durations and per-element class maps — CSS transitions handle the rest.
Maintainers
Readme
use-animation-sequence
A React hook for orchestrating multi-step CSS class animations. Declare frame durations and per-element class maps. CSS transitions handle the rest.
Works with Tailwind CSS, plain CSS, or any class-based styling.
Install
npm install use-animation-sequenceQuick example
import { useAnimationSequence } from "use-animation-sequence"
function PulsingCard() {
const { classes } = useAnimationSequence({
frames: [800, 2400, 800],
transition: "transition-all duration-500 ease-in-out",
autoPlay: true,
loop: true,
})
return (
<div className={classes("opacity-50 scale-95", "opacity-100 scale-100", "opacity-50 scale-95")}>
Content
</div>
)
}Multiple elements, one timeline
Each element gets its own class map while sharing the same timing sequence:
function AnimatedHeader() {
const { classes } = useAnimationSequence({
frames: [600, 2000, 600],
transition: "transition-all duration-500",
autoPlay: true,
loop: true,
})
return (
<div>
<div className={classes("h-0", "h-8", "h-0")} />
<h1 className={classes("opacity-0", "opacity-100", "opacity-0")}>
Title
</h1>
<p className={classes("translate-y-4", "translate-y-0", "translate-y-4")}>
Subtitle
</p>
</div>
)
}Per-frame transition overrides
Override the transition for specific frames:
const { classes } = useAnimationSequence({
frames: [500, 1000, 500],
transition: "transition-all duration-300",
autoPlay: true,
})
<div className={classes(
"opacity-0",
{ classes: "opacity-100", transition: "transition-opacity duration-1000" },
"opacity-0"
)} />Manual controls
const { classes, frame, isPlaying, next, prev, goTo, play, pause, reset } =
useAnimationSequence({
frames: [500, 500, 500],
})
// Step through manually
<button onClick={next}>Next</button>
<button onClick={prev}>Previous</button>
<button onClick={() => goTo(2)}>Jump to frame 2</button>
// Playback controls
<button onClick={play}>Play</button>
<button onClick={pause}>Pause</button>
<button onClick={reset}>Reset</button>API
useAnimationSequence(options)
Options
| Option | Type | Default | Description |
|---|---|---|---|
| frames | [number, number, ...number[]] | required | Duration in ms for each frame (minimum 2 frames) |
| transition | string | "" | Default transition classes applied to all frames |
| autoPlay | boolean | false | Start playing immediately |
| loop | boolean | false | Loop back to frame 0 after the last frame |
| onFrameChange | (index: number) => void | - | Called when the active frame changes |
| onComplete | () => void | - | Called when the sequence ends (non-loop only) |
Returns
| Property | Type | Description |
|---|---|---|
| classes | (...args: FrameClasses[]) => string | Returns resolved classes for the current frame |
| frame | number | Current frame index |
| isPlaying | boolean | Whether auto-play is active |
| next | () => void | Advance to next frame (clamps at last) |
| prev | () => void | Go to previous frame (clamps at 0) |
| goTo | (index: number) => void | Jump to a specific frame |
| play | () => void | Start auto-play |
| pause | () => void | Pause auto-play |
| reset | () => void | Reset to frame 0 and stop |
FrameClasses
Each argument to classes() can be:
string- CSS classes for that frame{ classes: string, transition?: string }- Classes with a transition overridenull- Carry forward the previous frame's classes
How it works
The hook manages a frame state machine with configurable durations. It doesn't animate anything itself. It just decides when different CSS classes should be active. CSS transitions (via Tailwind or plain CSS) handle the actual visual animation.
This means you get the full power of CSS transitions (hardware acceleration, easing curves, property-specific durations) with a simple declarative API for orchestrating multi-step sequences.
Works with AI-generated code
AI tools can declare frame timings and class maps without manually wiring up useState, useEffect, setTimeout, and cleanup logic. The complexity lives in the hook, not in every component.
License
MIT
