Shots in this scene
auto-animate in One Line, and Its Limits
In one line
autoAnimate(parent) watches a parent's children and animates adds, removes and moves with FLIP, in one line and about 3.3 kB gzipped. It is the right tool for simple lists, and you should know what it does not give you.
Think of it like this
A stagehand who watches the stage and, whenever a prop is added, removed or moved, slides it smoothly without being told how. You never write the choreography, which is wonderful until you want a specific one. Then you have to take the job back.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- MutationObserver
- A browser object that calls your function when the page's DOM changes, for example when a child is added to a list.
- ResizeObserver
- A browser object that calls your function when an element's size changes.
- controller
- The object
autoAnimate()returns, withenable,disable,isEnabledanddestroy. - plugin
- A function you pass instead of options, which returns your own keyframes for add, remove and remain.
Step by step
01Run the one-line list
Run "One line of code". The console says enabled: true | parent position: relative: the library set the parent's position for you.
02Add an item
Pause and press Add. The new li has a track [opacity, transform] of 375 ms with ease-in, and every other item has a 250 ms transform track. The list itself has a red [height, transform] track too: the parent grows by animating its height.
03Remove an item
Press Remove first. The removed item is position: absolute while it fades (250 ms, ease-out), and a red ul track [height, transform] appears: the parent shrinks by animating its height.
04Options and the switch
In "Options, the switch and reduced motion" the duration is 600 ms. Press Turn off, then Remove first: the list changes at once, document.getAnimations() is empty. Turn on Reduced motion and Run: the console says enabled: false.
Try it yourself
- 1
Read the defaults
Pause, press Add in the first example and click the new item's track:
opacityandtransform, 375 ms,ease-in. Click another item's track:transform, 250 ms,ease-in-out. - 2
Run as a reduced-motion user
Switch on Reduced motion in the timeline, run the second example and press Add: the console shows
reduced motion asked: true | enabled: falseand no animation plays. - 3
Override it
Add
disrespectUserMotionPreference: trueto the options object, switch on Reduced motion, and run: the console saysenabled: true. This is a way to ignore a visitor's setting, so you should only use it for motion that carries meaning.
Code & diagrams
Add, remove and reverse. Pause and read the tracks, especially the red one on the list itself.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Expected console output
enabled: true | parent position: relativeA 600 ms duration, a Turn off button and a reduced-motion check. Switch on the timeline's Reduced motion and run it again.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Expected console output
reduced motion asked: false | enabled: trueBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Animate a parent that is not the list
In the first example change the call to autoAnimate(document.body).
Myth vs fact
Myth
auto-animate respects the user's reduced-motion setting for as long as the page is open.
Fact
It checks once, when you call it. A visitor who changes the setting afterwards, or uses the site's toggle, is not noticed unless you call disable() yourself.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
The library animates
widthandheightwhen an element changes size, which are layout properties. Even a plain list gets one layout-cost track, the parent's height, on every add or remove (measured above), and cards that resize animate on the main thread too. - ▸
Elements outside the viewport are skipped (
isOffscreen), so a very long list costs less than it looks. The same idea is in ourflip(): read everything, animate only what you can see. - ▸
In React,
useAutoAnimatefrom@formkit/auto-animate/reactreturns a[ref, enable]pair and cleans up on unmount.
Remember this
- 1
The problem this solves: the FLIP code of Topics 10.1 and 10.2 is about 60 lines you must write, test and keep correct. For a todo list or a menu you often want the effect and no code.
@formkit/auto-animate(version 0.10.0 here, MIT licence) gives it in one line:autoAnimate(listElement). - 2
How it works inside. It puts a
MutationObserveron the parent's child list. When a child is added, removed or reordered, it reads each child's box (First, which it stored after the last change) and the new box (Last), and plays a WAAPI animation: a FLIP. It also uses aResizeObserverand anIntersectionObserverto keep the stored boxes current. - 3
What each change gets by default (read from the library's source and from the timeline). Elements that stay:
translatefrom their old place, 250 msease-in-out. A new element:scale(0.98)and opacity 0 held for the first half, then to 1, over 375 ms (1.5 times the duration) withease-in. A removed element: put back in the parent asposition: absoluteat its old place, faded to 0 withscale(0.98)over 250 ms withease-out, then removed. It setsposition: relativeon the parent if that was static. - 4
What it costs. When an element's size changes,
remainanimateswidthandheightas well, and when something is added or removed the parent'sheightanimates. The timeline shows the parent's track as red (layout). It also skips elements that are off-screen. - 5
Reduced motion is checked once. When
autoAnimate()is called it asksmatchMedia("(prefers-reduced-motion: reduce)"). If the answer is yes, it does nothing: no observer, andcontroller.isEnabled()isfalse. It does not follow a later change of the setting, and it knows nothing about Phase 7's site toggle. Call it only whenmotionAllowed()is true, and callcontroller.disable()when that changes. PassdisrespectUserMotionPreference: trueonly for a motion that is essential. - 6
The controller.
autoAnimate()returns{ parent, enable, disable, isEnabled, destroy }.disable()cancels animations in flight.destroy()disconnects every observer: call it when the list leaves the page, or the observers stay alive. - 7
Its limits. It works on direct children of one parent. It cannot move an element between two parents, share an element between two views (that is View Transitions, Phase 11, or a layout animation in Motion, Phase 15), pick a different exit per item, or run a timeline. You can pass a plugin function instead of options to write your own keyframes. When you reach those limits, the hand-written
flip()is the next step.
Explain it without notes
How does auto-animate work, and which three kinds of change does it animate?
Name two limits of auto-animate and what you would use instead.
Practice
Write a mountList(el) that turns auto-animate on only when motion is allowed and turns it off if that changes.
How would you make auto-animate's duration 400 ms with ease-out?
Trade-offs
- ↔
One line against control. auto-animate is a good default for simple lists and a poor fit for choreography. Our own
flip()is 60 lines and does exactly what the page needs.
Done when you can
You can say what auto-animate animates and how
You can read its default durations and easings
You can handle reduced motion and cleanup
You can name its limits