Smooth Layout Transitions in Framer Motion: Using the `layout` Prop for Responsive Card Grids
Discover how Framer Motion's `layout` prop automatically animates element repositioning and resizing in a responsive card grid, plus performance trade‑offs and a verification checklist.
03 Feb 2026, 06:59 UTC

The problem: janky card grids
When a card in a responsive grid expands, collapses, or moves to a new row, the browser has to recalculate layout for every sibling. Without help, the change snaps instantly, producing a visual "pop" that feels broken on modern interfaces.
Thesis: let Framer Motion do the math
The layout prop on a motion component tells Framer Motion to measure the element before and after a render, then interpolate the difference with a spring. No keyframes, no manual FLIP calculations—just a single prop.
How the layout prop works
- Pre‑layout capture – On each render Framer Motion reads the element’s bounding rectangle (position, width, height, margins).
- Spring interpolation – The library creates a spring animation (default stiffness = 500, damping = 30) that drives a transform from the old rectangle to the new one.
- Post‑layout commit – When the spring settles, the transform is removed and the element sits at its natural layout position.
Only properties that affect geometry (width, height, margin, transform) are animated. Opacity, color, or box‑shadow still need explicit animate or variants.
Worked example: responsive card grid
Below is a minimal React component that toggles a card’s expanded state. The grid reflows automatically, and each card animates its new position and size.
import { motion } from "framer-motion";
import { useState } from "react";
const cards = [
{ id: 1, title: "Alpha" },
{ id: 2, title: "Bravo" },
{ id: 3, title: "Charlie" },
{ id: 4, title: "Delta" },
];
export default function CardGrid() {
const [openId, setOpenId] = useState<number | null>(null);
return (
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))", gap: 16 }}>
{cards.map((c) => (
<motion.div
key={c.id}
layout
style={{
background: "#fff",
border: "1px solid #ddd",
borderRadius: 8,
padding: 16,
cursor: "pointer",
// height grows when open
height: openId === c.id ? 200 : 120,
}}
onClick={() => setOpenId(openId === c.id ? null : c.id)}
transition={{ type: "spring", stiffness: 500, damping: 30 }}
>
<h3 style={{ margin: 0 }}>{c.title}</h3>
{openId === c.id && <p>Expanded content for {c.title}</p>}
</motion.div>
))}
</div>
);
}
Key points in the snippet:
layouton each card enables automatic FLIP animation.- The grid uses
auto-fillso cards wrap responsively; when one card grows, siblings slide to new cells. transitionoverrides the default spring if you need a snappier or softer feel.
Trade‑offs and limitations
| Concern | Impact | Mitigation |
|---|---|---|
| Large lists (hundreds of items) | Each layout change triggers a measurement pass; can cause frame drops on low‑end devices. | Disable layout on virtualized rows, or add will-change: transform only to visible items. |
Elements with position: absolute or portals | Framer Motion cannot reliably measure them because they’re removed from normal flow. | Use layoutId for shared‑element transitions, or animate manually with animate. |
| Nested layout components | Deep nesting multiplies reflow work. | Keep layout trees shallow; promote only the outermost wrapper to layout when possible. |
Unique layoutId values | Duplicate IDs cause cross‑component morphing. | Generate IDs from stable keys (e.g., database IDs) and scope them per feature. |
Actionable implementation checklist
- Add
layoutto the motion component that changes size or position. - Verify the animation in Chrome DevTools → Performance tab: record a click, expand the Animations track, and confirm a single spring entry per card.
- Open the Rendering panel, enable Paint flashing to ensure only the moving cards repaint.
- Throttle CPU (6× slowdown) and repeat the interaction; if frames drop below 30 fps, consider disabling
layoutfor off‑screen items or reducing spring stiffness. - Run the same test on a low‑end device (or Chrome’s device toolbar with "Mobile CPU") to confirm acceptable performance.
Closing thought
The layout prop turns a traditionally painful layout‑thrashing problem into a one‑line opt‑in. For most responsive grids, card drawers, or sidebar toggles it delivers buttery‑smooth motion with virtually no extra code. Just remember to measure on the target hardware, keep the animated tree shallow, and fall back to manual animations when you hit the edge cases outlined above.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.