Choosing Between Framer Motion's layout Prop and Manual animate/transition for Shared Layout Animations
Guide to choosing Framer Motion's layout prop versus manual animate/transition for shared layout animations, with a comparison table, trade-offs, and a reorderable list example.
20 Jul 2026, 10:23 UTC

Decision and constraints
When you need to animate a change in a component's position or size after a re-render (for example, re-ordering a list, showing/hiding a panel, or resizing a card), Framer Motion offers two main ways to achieve the effect:
- Use the
layoutprop, which automatically detects changes in layout-triggering CSS properties and animates them with the FLIP technique (measure First and Last positions, Invert, then Play). - Manually specify the animated values with
animateandtransitionprops (often combined withvariants).
The decision hinges on how much control you need over individual properties, how much boilerplate you're willing to write, and whether you risk animating the same property twice (which can cause jank).
Comparison of supported options
| Aspect | layout prop | Manual animate/transition |
|---|---|---|
| What it animates automatically | Position, size, and any transform-related layout changes (width, height, top, left, margin, padding, etc.) | Only the properties you list in animate; you must include every layout-triggering change you want to see |
| Boilerplate | Minimal — just add layout (and optionally transition) to the component | Higher — you need to define animate (or variants) and list each changing style |
| Control over individual properties | Limited — all layout-triggering changes share the same transition unless you override with additional animate props | Fine-grained — you can set different durations, easings, or delays per property |
| Risk of double work | Low if you avoid manually animating the same layout properties | High if you accidentally animate a layout property both via layout (if used elsewhere) and via animate |
| Compatibility with CSS-in-JS or external stylesheets | Requires that the component's layout be measured by Framer Motion; styles that bypass the measurement phase (e.g., a transform from a stylesheet applied after mount) can break the FLIP measurement | Works as long as the animated properties are accessible to Framer Motion; external transforms still need to be overridden via animate if you want them animated |
Trade-offs explained
When the layout prop is the better choice
- Simple re-ordering or insertion/removal of siblings where the only changes are position and size.
- You want to reduce code duplication and avoid forgetting to list a property.
- Performance is critical and you rely on the built-in FLIP optimisation to keep motion near 60 fps.
When manual animate/transition is preferable
- You need to animate non-layout properties (color, opacity, filter) alongside layout changes.
- Different properties require distinct timing or easing curves (e.g., a quick fade with a slow slide).
- You are working with a CSS-in-JS solution that injects transforms after the initial render and you cannot guarantee Framer Motion's measurement phase will see them.
Concrete implementation and validation
The following example shows a reorderable list built with the react-beautiful-dnd library. Two versions are presented side-by-side: one using the layout prop, the other using manual animate props. You can swap the useLayout flag to see the difference. Run this inside a standard React app where framer-motion and react-beautiful-dnd are installed; no special permissions are needed beyond a normal development server.
import React from 'react';
import { motion } from 'framer-motion';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
function List({ items, useLayout = true }) {
const [list, setList] = React.useState(items);
const handleDragEnd = (result) => {
if (!result.destination) return;
const newList = Array.from(list);
const [moved] = newList.splice(result.source.index, 1);
newList.splice(result.destination.index, 0, moved);
setList(newList);
};
return (
<DragDropContext onDragEnd={handleDragEnd}>
<Droppable droppableId="list">
{(provided) => (
<motion.ul
ref={provided.innerRef}
{...provided.droppableProps}
style={{
display: 'flex',
flexDirection: 'column',
gap: '8px',
padding: '16px',
}}
>
{list.map((item, idx) => (
<Draggable key={item.id} draggableId={item.id} index={idx}>
{(provided, snapshot) => (
<motion.li
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={{
userSelect: 'none',
padding: '12px',
background: snapshot.isDragging ? '#e0f7ff' : '#fff',
border: '1px solid #ddd',
borderRadius: '4px',
width: '100%',
}}
{/* Decision point: layout prop */}
layout={useLayout}
{/* Manual alternative (remove layout when using this):
animate={{ x: 0, y: 0, width: '100%', height: 'auto' }}
transition={{ type: 'spring', stiffness: 300, damping: 20 }}
*/}
>
<div style={{ flex: 1 }}>{item.label}</div>
</motion.li>
)}
</Draggable>
))}
{provided.placeholder}
</motion.ul>
)}
</Droppable>
</DragDropContext>
);
}
To validate that both approaches produce visually identical motion:
- Render the list with
useLayout={true}and perform a drag-and-drop reorder. - Open the browser's Performance panel, record the interaction, and check that the frame rate stays near 60 fps with no long layout tasks.
- Change
useLayouttofalseand enable the manualanimateblock, ensuring you list every property that changes (in this casex,y,width, andheight). - Repeat the drag-and-drop and compare the recorded frames; the visual trajectory should match the layout-prop version.
- Run the app with
React.StrictModein development and verify that no console warnings appear about missing animation targets.
Limitations and practical checks
- The
layoutprop cannot animate non-layout properties such ascolororopacityon its own; you must add an additionalanimateprop for those. - If you manually animate a layout property while
layoutis also active, you may see duplicated work and occasional jank. The practical check is to look for layout thrashing in the Performance panel (multiple forced synchronous layouts per frame). - When using CSS-in-JS libraries that inject transforms after the initial render (e.g., via a theme change), ensure the component's layout is measured before the transform is applied, or disable
layoutand animate the transform manually.
By following the table, trade-off discussion, and validation steps above, you can decide which approach fits your animation needs and verify that the implementation behaves as expected without introducing performance regressions.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.