Tiny stateful animation sequence engine on top of framer-motion.
npm install @belkajo/motion-engineA tiny scene-based animation engine built on top of framer-motion. Designed for multi-step UI animations where each UI element evolves through states and coordinated transitions.
> Status: Experimental โ API may change.
---
- Scene builder (Scene) for structured multi-step animations.
- Incremental variants: later steps extend previous animation states.
- State extraction per step with buildStatesFor().
- Sequence controller (useSequence) automatically advances when all animated elements finish.
- Animated wrapper (AnimatedElement) integrates sequencing with framer-motion.
---
In a pnpm monorepo:
``bash`
pnpm add @belkajo/motion-engine --filter
---
`ts
import { Scene } from "@belkajo/motion-engine";
const scene = new Scene()
.addStep({
form: {
variant: { opacity: 0, y: -40 },
state: {}
}
})
.addStep({
form: {
variant: { opacity: 1, y: 0 },
state: {}
}
});
`
`ts`
const variants = scene.buildVariantsFor("form");
Produces merged variants:
`ts`
{
0: { opacity: 0, y: -40 },
1: { opacity: 1, y: 0 }
}
`ts`
const states = scene.buildStatesFor("form");
---
`ts
import { useSequence } from "@belkajo/motion-engine";
const { currentStep, onStart, onDone, setCurrentStep } = useSequence(scene);
`
- currentStep โ active animation stepsetCurrentStep(n)
- โ manually jump to a steponStart(id)
- โ mark an element as animatingonDone(id)
- โ mark animation complete and advance step when all finished
---
Give time to finish the animations after reset before starting the animation scenes.
Use instead of motion.div to integrate sequencing:
`tsx`
step={currentStep}
onStart={onStart}
onDone={onDone}
>
Note on stability
AnimatedElement generates a stable internal id (via useMemo) which is used by useSequence
to track running animations. This is intentional.
Avoid conditionally changing or remounting individual AnimatedElements unless you explicitly
want to cancel their animations.
---
`tsx
const scene = new Scene()
.addStep({ form: { variant: { opacity: 0 }, state: {} } })
.addStep({ form: { variant: { opacity: 1 }, state: {} } });
const variants = scene.buildVariantsFor("form");
const { currentStep, onStart, onDone } = useSequence(scene);
return (
step={currentStep}
onStart={onStart}
onDone={onDone}
>
---
๐ Types
`ts
interface StepConfig {
variant: Variant;
state: TState;
}
``---