EmpireUI
Get Pro
← Blog7 min read#framer-motion#layout-animation#react-animation

Framer Motion Layout Animations: Shared, Animate Presence

Framer Motion layout animations let you animate DOM reflows without hacks. Learn shared layout, AnimatePresence, and layoutId with real TSX examples.

Abstract smooth motion blur transition representing layout animation in React

What Are Framer Motion Layout Animations

Honestly, most animation libraries treat layout changes like they don't exist. You move an element across the page, swap a list order, or expand a card — and the browser just teleports it. No transition. No sense of continuity. Framer Motion's layout prop changes that.

The core idea is simple: add layout to a motion component, and Framer Motion will automatically animate any changes to that element's size or position caused by a React re-render. It uses the FLIP technique (First, Last, Invert, Play) under the hood, measuring before and after positions and interpolating the delta as a transform — so you get GPU-accelerated transitions without writing a single keyframe.

This works with Framer Motion v11.x, which ships as framer-motion on npm. You don't need any extra configuration. If you've already got it in your project, layout animations are one prop away.

The layout Prop: Animating Reflows Automatically

The layout prop is the entry point. Add it to any motion.div (or any other motion element) and it starts watching for layout changes. Expand a sidebar? The sibling content reflows smoothly. Toggle a filter? The list items slide to their new positions instead of jumping.

You can scope it too. layout="position" only animates the element's position, leaving size changes instant. layout="size" does the opposite. And plain layout animates both. That granularity matters when you're dealing with text containers — animating size on a <p> tag often looks wrong because the text reflows mid-animation.

One gotcha: layout animations can conflict with CSS transitions on the same element. If you've got a transition: all 0.3s somewhere in your stylesheet, you'll get weird double-animation artifacts. Strip those out and let Framer Motion own the transition via its transition prop.

layoutId: Shared Layout Animations Between Components

This is where things get genuinely interesting. layoutId lets you animate an element from one place in the DOM to another — even across completely different components. Give two motion elements the same layoutId string, and when one unmounts and the other mounts, Framer Motion creates a smooth transition between them.

The classic use case is a selected-item indicator, like an underline that slides between tabs. But you can do a lot more: expand a card from a grid into a modal, animate a thumbnail into a full-size hero image, or morph a button into an inline form.

import { motion, AnimatePresence } from 'framer-motion';

const tabs = ['Overview', 'Details', 'Reviews'];

export function TabBar({ active, onSelect }: { active: string; onSelect: (t: string) => void }) {
  return (
    <nav className="flex gap-6 border-b border-white/10">
      {tabs.map((tab) => (
        <button
          key={tab}
          onClick={() => onSelect(tab)}
          className="relative pb-2 text-sm font-medium text-white/70 hover:text-white"
        >
          {tab}
          {active === tab && (
            <motion.span
              layoutId="tab-underline"
              className="absolute bottom-0 left-0 right-0 h-0.5 bg-indigo-500"
              transition={{ type: 'spring', stiffness: 380, damping: 30 }}
            />
          )}
        </button>
      ))}
    </nav>
  );
}

Notice the transition config: stiffness: 380, damping: 30 gives a snappy spring that feels physical without overshooting. If you want something looser, try stiffness: 200, damping: 20. These values matter more than most people admit.

AnimatePresence: Animating Mount and Unmount

React doesn't give you a hook for "element is about to unmount." The component just disappears. AnimatePresence solves this by keeping components in the DOM until their exit animation completes, then removing them.

Wrap your conditionally-rendered content in <AnimatePresence> and give the child motion components an exit prop. That's the animation that plays on unmount. You also need a key on the child so Framer Motion can track when the component actually changes.

import { motion, AnimatePresence } from 'framer-motion';
import { useState } from 'react';

export function ToastNotification({ message }: { message: string | null }) {
  return (
    <AnimatePresence>
      {message && (
        <motion.div
          key={message}
          initial={{ opacity: 0, y: 20, scale: 0.95 }}
          animate={{ opacity: 1, y: 0, scale: 1 }}
          exit={{ opacity: 0, y: -10, scale: 0.95 }}
          transition={{ duration: 0.2 }}
          className="fixed bottom-6 right-6 rounded-xl bg-zinc-900 px-4 py-3 text-sm text-white shadow-xl"
          style={{ border: '1px solid rgba(255,255,255,0.08)' }}
        >
          {message}
        </motion.div>
      )}
    </AnimatePresence>
  );
}

The mode prop on AnimatePresence controls what happens when one child replaces another. mode="wait" exits the old one before entering the new one — good for page transitions. mode="popLayout" (added in v10) removes the exiting element from the layout flow immediately, so surrounding content doesn't wait for it.

If you're building UI with lots of conditional content, pairing AnimatePresence with something like a glassmorphism card can make the whole interface feel much more polished.

Combining layout and AnimatePresence for List Animations

Here's where the two features really pay off together. You've got a list. Items get added, removed, and reordered. Without any special handling, additions pop in, removals vanish, and reorders are instant jumps. With layout + AnimatePresence, each item animates to its new position, exits smoothly, and new items fade or slide in.

The pattern is straightforward: wrap the list in AnimatePresence, give each motion list item a stable key, add the layout prop, and define initial/animate/exit variants. The key absolutely must be stable and unique — using array index as key will break the animation entirely because Framer Motion won't know which item left.

One thing that trips people up: if the list container itself changes size as items are added or removed, you'll want to add layout to the container too, or wrap it in its own motion element. Otherwise the container snaps while the children animate, which looks off.

LayoutGroup: Coordinating Layout Animations Across the DOM

By default, layout animations are scoped to a single motion component. But what if you've got two separate components that both respond to the same state change? LayoutGroup lets you group them so their layout animations are synchronized.

This comes up with things like accordions inside a sidebar: when one panel expands, other panels push down, and you want all of those position changes to animate together instead of half of them jumping. Wrap the parent that contains all of them in <LayoutGroup> and add layout to each moving piece.

It's also worth knowing that layoutId automatically works across LayoutGroup boundaries. If you're building something like a spotlight effect component where an element needs to track across multiple sections, grouping them ensures the shared-element transition doesn't flicker.

Performance: What to Watch Out For

Layout animations are not free. Every time a layout animation triggers, Framer Motion reads the DOM to get bounding rects, which forces a layout recalculation. If you've got dozens of layout components all responding to the same state change, you'll feel it.

The fix isn't to stop using layout animations — it's to be deliberate. Only put layout on elements that actually need to animate their position or size. Don't blanket-apply it. And avoid triggering layout animations on every keystroke or scroll event; debounce those state changes or use useReducedMotion to skip animations for users who've requested it.

Also watch your z-index stacking during shared-layout transitions. Framer Motion promotes layoutId elements to a temporary overlay during the transition, which can cause clipping issues if parent elements have overflow: hidden. Set overflow: visible on the relevant ancestors, or restructure the DOM so the animated element isn't trapped inside a clipping container. You'll run into this eventually — better to know now.

If you're mixing layout animations with heavily animated backgrounds like particle effects or aurora backgrounds, profile the page in Chrome DevTools first. Compositing costs add up fast.

Practical Patterns Worth Stealing

Card-to-modal expansion is the one everyone reaches for first. A card in a grid has layoutId="card-{id}". Click it, render a modal with a motion div using the same layoutId. Wrap the conditional modal in AnimatePresence. Done — the card morphs into the modal and back. The trick is making sure both the card and the modal render the same child structure inside the layoutId element, otherwise you get a jarring content swap mid-transition.

Tab content transitions are another high-value pattern. AnimatePresence mode="wait" between tab panels, with a subtle x offset on initial and exit that changes direction depending on which tab you're navigating to. You can track the previous tab index with a ref and flip the sign accordingly.

Reorderable lists — drag-and-drop or programmatic — work beautifully with layout and Framer Motion's Reorder components (motion/react v11 ships these). They handle the drag gesture, the layout snap, and the exit/enter animations in one package. Worth pairing with a theme toggle so users can switch to a reduced-motion preference at runtime.

One last thing: don't sleep on useAnimate from Framer Motion for imperative layout animations. Sometimes you don't want React re-render to drive the animation — you want to fire it programmatically in response to a user gesture. useAnimate lets you do that while still using all the same spring physics.

FAQ

Does the layout prop work with CSS Grid and Flexbox reflows?

Yes. Framer Motion measures the element's actual bounding rect before and after the re-render, so it doesn't matter whether the reflow is caused by grid, flexbox, or absolute positioning changes. The FLIP approach works at the pixel level, not the layout-model level.

Why does my layoutId transition flash or jump instead of animating smoothly?

Usually it's an overflow: hidden on a parent element clipping the transitioning component. Framer Motion needs the animated element to be visible during the transition. Also check that the two elements sharing the layoutId have matching DOM structures — mismatched children can cause a visible content swap mid-animation.

Can I use AnimatePresence with React Router for page transitions?

Yes. Wrap your <Routes> (or equivalent) in <AnimatePresence mode="wait">, give each route's root motion element a key matching the current pathname, and define initial/animate/exit props. Make sure the key changes on every route change or AnimatePresence won't detect the swap.

What's the difference between layout="position" and layout="size"?

layout="position" only animates the element's x/y position changes — size changes happen instantly. layout="size" only animates width/height changes — position changes happen instantly. Plain layout animates both. Use the scoped versions when animating text containers where mid-animation text reflow looks broken.

Does useReducedMotion automatically disable layout animations?

Not automatically — Framer Motion respects the prefers-reduced-motion media query for variants and transition defaults, but layout animations still fire. You need to explicitly check const shouldReduceMotion = useReducedMotion() and conditionally omit the layout prop or set transition={{ duration: 0 }} when it's true.

Can I nest AnimatePresence components?

You can, but it gets complex fast. Each AnimatePresence manages its own exit queue independently. Nested ones don't coordinate — so if an outer AnimatePresence removes a parent, the inner one's exit animations may not play because the parent is gone. In practice, restructure your component tree to avoid deep nesting of AnimatePresence.

Free components in 40 styles
React & Tailwind, copy-paste ready.
Browse →

Read next

Framer Motion Page Transitions: Full Next.js App Router ExampleCSS Animations & Motion Design: The Complete 2026 PlaybookAnimated Tab Indicator: Sliding Underline with Layout AnimationMotion Design System: Timing, Easing, and Token Standards