| name | animejs |
| description | Use this skill whenever Anime.js is involved in any way — adding animations to a project, writing Anime.js code, debugging Anime.js behavior, integrating it with React/Next.js, building scroll-triggered effects, timelines, staggered reveals, SVG path drawing, or combining Anime.js with other libraries like GSAP or Framer Motion. Trigger this skill any time the user mentions "anime.js", "animejs", "animate()", stagger animations, timeline sequencing, or asks how to animate elements in a frontend project where Anime.js is available. Always use this skill before writing any Anime.js code — it contains critical v4 API changes that differ from v3.
|
Anime.js Skill
Anime.js is a lightweight (~10KB) JavaScript animation engine that works with CSS properties,
SVG attributes, DOM attributes, and plain JS objects. This skill covers v4 (latest).
v4 is a breaking rewrite from v3 — always write v4 code unless the user explicitly asks for v3.
Installation
npm i animejs
Core v4 Import Pattern
import { animate, timeline, stagger, createTimer, createSpring, utils, svg } from 'animejs'
Never use import anime from 'animejs' — that's v3 syntax and will break.
v3 → v4 Critical Differences (READ BEFORE WRITING ANY CODE)
| v3 | v4 |
|---|
import anime from 'animejs' | import { animate } from 'animejs' |
anime({ targets: '.el', ... }) | animate('.el', { ... }) |
easing: 'easeInOutQuint' | ease: 'inOutQuint' |
opacity: { value: 0.5 } | opacity: { to: 0.5 } |
direction: 'reverse' | reversed: true |
direction: 'alternate' | alternate: true |
endDelay | loopDelay |
change callback | onRender callback |
loop: 1 = run once | loop: 1 = repeat once (2 total iterations) |
delay: stagger(100, { direction: 'reverse' }) | delay: stagger(100, { reversed: true }) |
animate() — Basic Usage
import { animate } from 'animejs'
animate('.card', {
opacity: [0, 1],
translateY: [-20, 0],
duration: 600,
ease: 'outExpo',
delay: 200,
})
Tween value shorthands
translateX: 100
opacity: [0, 1]
opacity: { from: 0, to: 1, duration: 400, ease: 'inQuad' }
scale: [{ to: 1.2 }, { to: 1 }, { to: 1.1 }]
Playback parameters
animate('.el', {
x: 200,
duration: 1000,
delay: 500,
loop: true,
loop: 3,
alternate: true,
reversed: true,
autoplay: false,
ease: 'inOutQuart',
})
stagger() — Multiple Targets
import { animate, stagger } from 'animejs'
animate('.node', {
opacity: [0, 1],
translateY: [-10, 0],
delay: stagger(80),
delay: stagger(80, { from: 'center' }),
delay: stagger(80, { reversed: true }),
delay: stagger([0, 500]),
})
timeline() — Sequencing Animations
import { timeline } from 'animejs'
const tl = timeline({
defaults: { duration: 400, ease: 'outExpo' },
loop: false,
})
tl.add('.hero-title', { opacity: [0, 1], translateY: [-30, 0] })
.add('.hero-sub', { opacity: [0, 1], translateY: [-20, 0] }, '-=200')
.add('.hero-cta', { opacity: [0, 1], scale: [0.9, 1] }, '+=100')
.add('.hero-badge', { opacity: [0, 1] }, 0)
Time position syntax
| Value | Meaning |
|---|
| (none) | After previous animation ends |
'-=200' | 200ms before previous ends (overlap) |
'+=200' | 200ms after previous ends (gap) |
0 | Absolute time position (0ms from start) |
'labelName' | At a named label |
SVG Animation — Path Drawing
import { animate, svg } from 'animejs'
animate('path', {
strokeDashoffset: [svg.pathLength, 0],
duration: 1200,
ease: 'inOutSine',
})
const path = svg.morphTo('#target-path')
animate('.dot', {
x: path.x,
y: path.y,
duration: 2000,
ease: 'linear',
})
Scroll-Triggered Animations (onScroll)
import { animate, onScroll } from 'animejs'
animate('.section-card', {
opacity: [0, 1],
translateY: [40, 0],
duration: 700,
ease: 'outExpo',
delay: stagger(100),
autoplay: onScroll({
target: '.section-card',
enter: 'bottom 80%',
leave: 'top 20%',
sync: false,
}),
})
For scrubbed (parallax-style) scroll animations:
animate('.parallax-bg', {
translateY: [-50, 50],
autoplay: onScroll({
target: '.parallax-bg',
sync: true,
}),
})
React / Next.js Integration Pattern
Rules for React:
- Always use
useRef to target elements — never CSS class strings in React components
- Always run
animate() inside useEffect with a cleanup return
- Call
.revert() on cleanup to remove inline styles and prevent memory leaks
- Add
'use client' to any component that uses Anime.js
'use client'
import { useEffect, useRef } from 'react'
import { animate, stagger } from 'animejs'
export function AnimatedList({ items }: { items: string[] }) {
const containerRef = useRef<HTMLUListElement>(null)
useEffect(() => {
if (!containerRef.current) return
const anim = animate(
containerRef.current.querySelectorAll('li'),
{
opacity: [0, 1],
translateX: [-20, 0],
duration: 500,
ease: 'outExpo',
delay: stagger(60),
}
)
return () => anim.revert()
}, [items])
return (
<ul ref={containerRef}>
{items.map(item => (
<li key={item} style={{ opacity: 0 }}>{item}</li>
))}
</ul>
)
}
Important: Set initial opacity: 0 via inline style (not CSS class) so the element
is hidden before JS runs. Don't rely on CSS since hydration timing varies in Next.js.
Playback Control
const anim = animate('.el', { x: 200, autoplay: false })
anim.play()
anim.pause()
anim.restart()
anim.reverse()
anim.seek(500)
anim.cancel()
anim.revert()
anim.complete()
Callbacks
animate('.el', {
x: 200,
onBegin: (anim) => console.log('started'),
onComplete: (anim) => console.log('done'),
onUpdate: (anim) => console.log(anim.progress),
onRender: (anim) => {},
onLoop: (anim) => {},
})
Also supports Promise-style:
await animate('.el', { x: 200 })
Spring Physics
import { animate, createSpring } from 'animejs'
animate('.card', {
scale: [0.8, 1],
ease: createSpring({ stiffness: 300, damping: 15, mass: 1 }),
duration: 800,
})
Animatable — Reactive / Imperative Updates
Use when you need to update animation values on the fly (hover, drag, real-time):
import { createAnimatable } from 'animejs'
const el = createAnimatable('.cursor', {
x: { duration: 200, ease: 'outExpo' },
y: { duration: 200, ease: 'outExpo' },
})
document.addEventListener('mousemove', (e) => {
el.x(e.clientX)
el.y(e.clientY)
})
Common Patterns for Frontend Projects
Entrance animation on mount
useEffect(() => {
const anim = animate(ref.current, {
opacity: [0, 1],
translateY: [24, 0],
duration: 700,
ease: 'outExpo',
})
return () => anim.revert()
}, [])
Staggered card grid reveal
const anim = animate(
containerRef.current?.querySelectorAll('.card'),
{
opacity: [0, 1],
scale: [0.95, 1],
duration: 500,
delay: stagger(60, { from: 'center' }),
ease: 'outBack(1.2)',
}
)
Node pulse loop (for graphs, indicators)
animate(nodeRef.current, {
scale: [1, 1.06, 1],
duration: 2000,
loop: true,
ease: 'inOutSine',
})
SVG connector line draw-on
animate(pathRef.current, {
strokeDashoffset: [svg.pathLength, 0],
duration: 800,
ease: 'outExpo',
delay: stagger(150),
})
Hover interaction (with revert on mouse leave)
let hoverAnim: ReturnType<typeof animate> | null = null
el.addEventListener('mouseenter', () => {
hoverAnim = animate(el, { scale: 1.05, duration: 250, ease: 'outBack' })
})
el.addEventListener('mouseleave', () => {
hoverAnim?.pause()
animate(el, { scale: 1, duration: 200, ease: 'outExpo' })
})
Combining with Other Libraries
With GSAP ScrollTrigger
Use GSAP only for scroll trigger detection, Anime.js for the actual animation:
ScrollTrigger.create({
trigger: '.section',
onEnter: () => animate('.section .card', { opacity: [0,1], translateY: [30,0], delay: stagger(80) }),
})
With Framer Motion
Let Framer handle layout animations and exit animations.
Use Anime.js for micro-interactions, loops, and SVG drawing inside components.
Avoid animating the same property with both libraries on the same element.
With Three.js
Disable Anime.js's own loop and hook into Three.js's animation loop:
import { engine } from 'animejs'
engine.useDefaultMainLoop = false
function render() {
engine.update()
renderer.render(scene, camera)
}
renderer.setAnimationLoop(render)
Reduced Motion — Accessibility
Always check for prefers-reduced-motion:
const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches
if (!prefersReduced) {
animate('.el', { translateY: [-20, 0], opacity: [0, 1], duration: 600 })
} else {
animate('.el', { opacity: [0, 1], duration: 200 })
}
Easing Reference (v4 names)
'linear'
'out(n)' // generic ease-out, n = power (e.g. 'out(3)')
'in(n)' // generic ease-in
'inOut(n)' // generic ease-in-out
'outExpo' 'inExpo' 'inOutExpo'
'outQuart' 'inQuart' 'inOutQuart'
'outQuint' 'inQuint' 'inOutQuint'
'outBack(n)' 'inBack(n)' // overshoot, n = intensity (default 1.70158)
'outBounce' 'inBounce' 'inOutBounce'
'outElastic(n)' 'inElastic(n)' // spring-like
'inOutSine'
'steps(n)' // stepped/snap animation, n = number of steps
Docs Reference
Full documentation: https://animejs.com/documentation/
v3 → v4 migration: https://github.com/juliangarnier/anime/wiki/Migrating-from-v3-to-v4