Start here
Choose the right tool
CSS, Angular's native animate.enter and animate.leave, Angular Movement and raw WAAPI all have a place. Use the simplest one that does the job.
Quick decision guide
| You need… | Reach for |
|---|---|
| Hover colour, focus ring, spinner | CSS transitions / keyframes |
| One element fades or slides when it enters or leaves | animate.enter / animate.leave (or moveEnter / moveLeave) |
| A panel that must finish leaving before its parent is removed | Movement: *movePresence |
| A list where rows are added, removed and reordered | Movement: *movePresenceFor |
| A card that changes between idle / selected / error states | Movement: moveVariants or moveAnimation |
| Staggered reveal of siblings | Movement: moveStagger |
| Progress bound to scroll position | Movement: moveScroll (or CSS scroll-timeline where supported) |
| A single custom effect, nothing reused | Raw WAAPI |
CSS transitions and keyframes
If the motion is a function of a class or pseudo-class — a hover colour, a focus ring, a loading spinner, a menu that slides when a class is toggled — write CSS. It costs no JavaScript, works before hydration and is the easiest thing to maintain. Movement does not try to replace this, and you can mix both freely.
.button {
transition:
background-color 150ms ease,
transform 150ms ease;
}
.button:hover {
background-color: var(--accent-strong);
}
@media (prefers-reduced-motion: reduce) {
.button {
transition: none;
}
}
Angular's native animate.enter and animate.leave
Since Angular 20.2 the framework can apply CSS classes while a node enters and leaves the DOM, and @angular/animations is deprecated in favour of it. For many apps this covers the common case: each element has its own self-contained enter or leave effect described in CSS. It needs no extra dependency, and you should prefer it when that is all you need.
<!-- Angular adds the class on enter and waits for the animation to finish before removing the node on leave. -->
@if (open()) {
<section animate.enter="panel-enter" animate.leave="panel-leave">Details</section>
}
What it does not give you is coordination: no built-in way to hold a parent until all children have finished leaving, to reverse a keyed list item that is re-added while it is exiting, to stagger by DOM order, or to animate between two states of one element when a signal changes.
Angular Movement
Movement earns its place when motion is driven by state and must be coordinated:
- State to state —
moveVariantsand[moveAnimation]animate to a new target whenever a signal changes. - Presence —
*movePresenceand*movePresenceForkeep a view (or each keyed row) mounted until every leave animation finishes, including nested children and revival mid-exit. - Orchestration —
moveStaggerand variantstaggerChildrenfor DOM-order delays. - Interaction feedback —
moveWhileHover,moveWhileTapandmoveWhileFocus, with proper touch semantics. - Scroll, springs, SVG and signals —
moveScroll,moveSpringValue,movePathDrawandmoveTransform. - Imperative sequences —
MoveAnimator, with automatic reduced-motion handling and SSR no-ops.
<ul>
<li
*movePresenceFor="let item of items(); trackBy: byId"
move="fade-up"
[moveWhileHover]="{ y: [0, -2] }"
>
{{ item.label }}
</li>
</ul>
Raw WAAPI
element.animate() is fine for one isolated effect. You then own reduced motion, cancellation, committing final styles and server-side no-ops. Movement's engines and MoveAnimator are thin wrappers that do these things consistently.
Honest trade-offs
Warning
Movement plays on the Web Animations API, which depends on hydration: anything it animates in from a hidden state needs the app to have started. For above-the-fold entrances that must paint immediately, prefer CSS. See Accessibility and troubleshooting.
- It is an additional dependency (a small, tree-shakeable one). If CSS or native enter and leave cover your needs, you do not need it.
- Layout, drag and smooth-scroll APIs are still experimental. Prefer native scrolling.
Next
Choose an API inside Movement, or migrate from @angular/animations.