Anime.js for dynamic websites: how to use v4 with motion that respects users
Most anime.js tutorials still teach v3, so the code fails before a frame runs. Here is the v4 shape that works, the guard that keeps it kind to people who ask for less motion, and the sum that tells you when a list moves for too long.
What is anime.js, and how do you use it for dynamic websites?#
Anime.js is a free JavaScript animation library; in v4 you import animate, createTimeline and stagger by name, pass the targets first, and wrap the motion in a scope that honours reduced motion.
Its README calls it "a fast, multipurpose and lightweight JavaScript animation library". Because it moves CSS properties, SVG, DOM attributes and plain objects, it fits most front ends. And the npm page lists the MIT License, so a commercial site may use it.
So what is anime.js v4 like to write? First, there is no global anime object any more. Instead, each feature is its own import, and the targets come first in the call. Then a scope decides whether the motion plays at all, and that is the step most tutorials skip.
Why use anime.js for dynamic websites at all?#
The anime.js documentation, read in October 2026, sizes the full animate() at 10KB and its Web Animation API version at 3KB, so a dynamic website loads only the motion it imports.
Show data table
| Item | Value |
|---|---|
| animate(), the JavaScript engine | 10 KB |
| waapi.animate(), the Web Animation API version | 3 KB |
The Web Animation API build covers simple tweens at 3KB, and the full engine adds timelines at 10KB.
A dynamic website adds content after the first load, such as search results or a growing feed. Because anime.js takes a selector or a list of elements, one call animates whatever is on the page when it runs. So a timeline can sequence a panel, and a stagger can reveal a fresh batch of results one by one.
And scroll is covered too. The onScroll page passes a ScrollObserver into the autoplay setting, so an element moves when it scrolls into view. Meanwhile, waapi.animate() hands the work to the browser, and the docs say it "covers most of the basic API". So pick the small build for simple tweens (one value animated from a start to an end) and the full engine for timelines.
Which official anime.js v4 pages should you build from?#
Build from the official pages in this order: installation, animate(), stagger, timeline add() and the Scope mediaQueries parameter, then MDN's prefers-reduced-motion entry.
- Page 1
Installation: npm install animejs, the module import and the CDN forms.
- Page 2
Animation: the animate(targets, parameters) call.
- Page 3
Time staggering: stagger() on delay.
- Page 4
Timeline add(): timeline.add(targets, parameters, position).
- Page 5
Scope mediaQueries: the reduceMotion query and self.matches.
- Page 6
MDN on prefers-reduced-motion: what the reduce value means.
The six pages, in order: Installation, Animation, Time staggering, Timeline add(), Scope mediaQueries, MDN on prefers-reduced-motion.
For older code, the migration guide is the seventh page.
How do you write your first anime.js v4 animation?#
Install animejs, import animate and stagger by name, and pass the targets as the first argument, because v4 has no global anime object and no targets key.
The migration guide says it plainly: "The targets parameter has been replaced by the mandatory Targets argument of the animate function." The code below assumes a bundler such as Vite or esbuild, which the installation page names. Without one, import from https://esm.sh/animejs instead.
// Step 1: npm install animejs (then bundle with Vite or esbuild)
// Step 2: import each function you use by name
import { animate, stagger, createTimeline } from 'animejs';
// Step 3: targets first, then parameters; stagger the delay
animate('.card', {
opacity: [0, 1],
y: [24, 0],
duration: 1250,
delay: stagger(65),
ease: 'inOutQuint',
});
// Step 4: sequence on a timeline; an optional third argument sets the start
createTimeline()
.add('.panel-title', { opacity: [0, 1], duration: 600 })
.add('.panel-body', { opacity: [0, 1], duration: 600 }); So each card starts 65 ms after the one before it. Then the timeline fades the title in, and the body follows. When new content arrives later, call the same animate() on the new elements once they are in the page.
How do you make anime.js respect prefers-reduced-motion?#
Wrap the animations in createScope with a reduceMotion media query and set the duration to 0 when it matches, the pattern the anime.js Scope documentation itself prints.
The Scope mediaQueries page "Defines the media queries to match for conditionally refreshing the Scope" when a match changes. And MDN says the reduce value "Indicates that a user has enabled the setting on their device for reduced motion."
import { createScope, animate, stagger } from 'animejs';
createScope({
mediaQueries: { reduceMotion: '(prefers-reduced-motion)' },
}).add(self => {
const { reduceMotion } = self.matches;
animate('.card', {
opacity: [0, 1],
y: [24, 0],
duration: reduceMotion ? 0 : 1250,
delay: reduceMotion ? 0 : stagger(65),
});
}); Why not trust the CSS reset in your stylesheet? Because that reset sets durations in CSS, while anime.js sets each frame from JavaScript, so the reset never reaches the tween. The W3C technique SCR40 takes this route: read the media query in JavaScript and stop the motion there. It supports WCAG 2.3.3, Animation from Interactions, a Level AAA criterion. Also, since the scope refreshes on a change, a user who turns the setting on mid-visit gets still content at once.
How long does an anime.js stagger run before it passes five seconds?#
In this worked example, at the anime.js README's 65 ms per element and 1250 ms duration, 24 cards settle at 2,745 ms and 59 cards run 5,020 ms, past five seconds. That five-second line is set by WCAG 2.2.2, which the W3C WAI publishes.
Total time is the step times the element count less one, plus the duration, starting from the first element (a stagger starts each element one fixed step after the one before). For instance, with the anime.js README values read on 10 October 2026, 24 cards give 65 × 23 + 1250, or 2,745 ms in this example. And 59 cards give 65 × 58 + 1250, or 5,020 ms.
Show data table
| Stage | Milliseconds until the last element settles |
|---|---|
| 4 elements | 1,445 ms |
| 12 elements | 1,965 ms |
| 24 elements | 2,745 ms |
| 48 elements | 4,305 ms |
| 58 elements | 4,955 ms |
| 59 elements | 5,020 ms |
At 65 ms a step and 1250 ms a tween, the run passes 5,000 ms between 58 and 59 cards.
Why does that line matter? WCAG 2.2.2, Pause, Stop, Hide, is a Level A criterion. It asks for a way to pause, stop or hide moving content that starts on its own, "lasts more than five seconds", and runs beside other content. Still, a one-time card reveal may not meet all three conditions. However, a looping list can, and the count that tips it is easy to miss.
When your stagger passes five seconds
Set your element count, stagger step and duration, and see when the last element settles and how far past five seconds it runs. Modelled, not measured.
Milliseconds until the last element settles
2,745
- Milliseconds past the five-second line
- 0
An illustrative model of the worked example above, not a measurement.
In practice, shorten the step as the list grows, or stagger only the cards in view. Either way, the guard above still drops it all to zero for anyone who asked.
Which websites are built with anime.js?#
The Wappalyzer anime.js page, read on 10 October 2026, ranks its top sites by traffic, and riverside.com and several TikTok business sites follow. So an anime.js website is a common production choice, not a demo.
Also, the project is maintained. The GitHub releases page, read on 10 October 2026, lists v4.5.0 as the newest release, published on 22 June. So the API you write against today is current.
Should you use anime.js or Motion?#
Choose anime.js for framework-free pages that sequence and stagger many elements, and Motion when a React or Vue app wants declarative components and a site-wide reducedMotion setting.
On npm the comparison is animejs vs motion, and both packages are MIT licensed. Motion's README offers "First-class React, JavaScript, and Vue packages" and notes that Framer Motion is now Motion. Its quick start, read on 10 October 2026, describes a mini animate() at 2.3kb. But Motion's npm page also sells Motion+, a paid licence for extra examples and premium APIs.
The real split is reduced motion. Motion's accessibility guide says that with reducedMotion="user" on MotionConfig, "all motion components will automatically disable transform and layout animations". So that is one switch for a whole React app. Anime.js has no such switch, so you write the scope guard around each group of animations. In return, its timelines and staggers are plain functions with no framework needed.
So the stack decides. For a static or server-rendered site, anime.js fits. For a React or Vue app built from components, Motion fits better. Other animation libraries exist, and the same test of stack and guard applies to them.
| Field | Anime.js | Motion |
|---|---|---|
| Stack it serves | Framework-free pages; timelines and staggers are plain functions | First-class React, JavaScript and Vue packages |
| Reduced motion | You write the scope guard around each group of animations | reducedMotion set to user on MotionConfig, one switch for a whole React app |
| Smallest build | waapi.animate() at 3KB | mini animate() at 2.3kb |
| Licence | MIT | MIT; Motion+ is a paid licence for extra examples and premium APIs |
What breaks when you paste a v3 anime.js example into v4?#
Most pasted v3 examples break on the same renames: targets moves into the first argument, easing becomes ease without its prefix, and direction becomes the alternate and reversed flags.
The migration guide lists each change, and these break most copied code:
Step 1: the import
import anime from 'animejs' fails, because the global anime object is replaced by the animate import.
Step 2: the targets
targets: '.box' moves out of the object to the first argument.
Step 3: the ease
"The easing parameter has been renamed ease", so easing: 'easeOutQuad' becomes ease: 'outQuad'.
Step 4: the direction
direction: 'alternate' becomes alternate: true, and direction: 'reverse' becomes reversed: true.
Step 5: the callbacks
endDelay becomes loopDelay, and the change callback becomes onRender.
So port one line at a time against that list, starting at the import line, where v3 code fails first.
When is anime.js the wrong tool for a dynamic website?#
Use a CSS transition for a single hover or fade, and Motion inside a React app, because anime.js earns its import when you sequence or stagger many elements.
First, a button hover or one fade needs no library. A CSS transition does it, and a prefers-reduced-motion block in the same stylesheet guards it, as MDN shows. Second, React layout animation is Motion's home ground, with the switch built in. Third, if you need a few simple tweens and every kilobyte counts, pick a smaller build. On 10 October 2026, the anime.js docs printed 3KB for waapi.animate(), and Motion's quick start printed 2.3kb for its mini animate().
Where do motion and accessibility go next on your site?#
Reduced motion is one of several user settings a site must honour, so visible focus and the WCAG version that binds you are the next two reads.
For keyboard users, read focus vs focus-visible in CSS. Then see which motion criteria apply at your level in the WCAG guidelines comparison. For the wider picture, start with the introduction to web accessibility. If you want a second pair of eyes, see accessibility testing or UI and UX design. Still, the anime.js documentation and the six pages above are enough to build this on your own.