Smooth List Reordering with Framer Motion's layout Prop
Learn how Framer Motion’s layout prop automatically animates list reordering without manual CSS, plus a ready‑to‑run React example and performance tips.
21 Mar 2026, 18:29 UTC

Problem: Animating list reorder without hand‑crafted CSS
When a React component reorders an array (e.g., via splice or sort), the DOM nodes move instantly. Users perceive a jump unless you write CSS transitions, manage getBoundingClientRect measurements, or orchestrate useEffect callbacks. This boilerplate adds complexity and is easy to get wrong, especially when items change size or are nested.
Thesis: Framer Motion’s layout prop eliminates the manual work
Setting layout={true} on a motion component tells Framer Motion to automatically measure the element’s position before and after a React render, then animate the difference using its built‑in spring physics. The animation runs with no extra lifecycle code, works with absolute or relative positioning, and respects existing CSS transforms.
How the layout prop works under the hood
When React schedules a state update that changes the rendered order, Framer Motion:
- Reads the current layout (
getBoundingClientRect) of eachmotionelement (the “read” phase). - Stores those values.
- After React commits the new render, it reads the new layout (the “write” phase).
- Computes the delta and runs a spring‑based animation from the old to the new values.
Because the measurement and animation are internal to Framer Motion, you only need to provide the layout flag.
Worked example: a shufflable list
The following snippet can be pasted into a fresh create‑react‑app project after installing Framer Motion (npm i framer-motion). No additional configuration is required.
import { motion } from 'framer-motion';
import { useState } from 'react';
export default function ShuffleList() {
const [items, setItems] = useState([
{ id: 1, label: 'Apple' },
{ id: 2, label: 'Banana' },
{ id: 3, label: 'Cherry' },
{ id: 4, label: 'Date' },
{ id: 5, label: 'Elderberry' },
]);
const shuffle = () => {
const copy = items.slice();
for (let i = copy.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[copy[i], copy[j]] = [copy[j], copy[i]];
}
setItems(copy);
};
return (
Shuffle
{items.map(item => (
{item.label}
))}
);
}
Where to run: Save the file as ShuffleList.jsx, import it into App.jsx, and start the dev server (npm start). No special permissions are needed beyond normal Node/npm access.
What to check: Open Chrome DevTools → Elements. Each motion.li will have a data-framer-motion attribute. In the Performance tab, record a shuffle; you should see a single paint per frame and a steady ~60 fps on a typical desktop.
Customizing the animation
You can adjust the spring via the transition prop:
<motion.li
layout
transition={{ type: 'spring', stiffness: 300, damping: 20 }}
>
{item.label}
</motion.li>
If you need to preserve an element’s identity when items share similar dimensions (e.g., two boxes of the same size), assign a unique layoutId:
<motion.li
layout
layoutId={`item-${item.id}`}
>
{item.label}
</motion.li>
Trade‑off and limitation
Layout animations rely on synchronous DOM reads. If an element is hidden (display: none) or removed from the DOM during the measurement phase, Framer Motion skips the animation, which can cause a sudden jump. Moreover, animating many items at once forces a layout‑thrash cycle (read‑write for each node), hurting performance on long lists.
Practical mitigation:
- Virtualize the list (e.g., with
react-window) so only visible rows are mounted and animated. - Disable layout animation for off‑screen items by conditionally setting
layout={false}. - Profile with the Performance panel; aim for under 16 ms per frame.
Actionable closing
Try the example above in your own project. Verify the animation with the DevTools steps, then experiment with transition values and layoutId to match your design language. If you notice frame drops on large datasets, introduce virtualization or limit animation to the viewport. Framer Motion’s layout prop gives you production‑grade reorder animations with minimal code—just remember to keep an eye on DOM measurement costs.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.