Motion
Back to framer.com
DocumentationAnimation
Layout
Motion
Back to framer.com
Design and publish your first free site today.
Getting started
  • Introduction
  • Examples
Animation
  • Overview
  • Layout
  • Gestures
  • Scroll
  • Transition
Components
  • motion
  • AnimatePresence
  • LayoutGroup
  • LazyMotion
  • MotionConfig
  • Reorder
Motion values
  • Overview
  • useMotionValueEvent
  • useMotionTemplate
  • useScroll
  • useSpring
  • useTime
  • useTransform
  • useVelocity
  • useWillChange
Hooks
  • useAnimate
  • useAnimationFrame
  • useDragControls
  • useInView
  • useReducedMotion
Universal
  • animate
  • transform
  • stagger
  • Easing functions
3D
  • Introduction
  • LayoutCamera
  • LayoutOrthographicCamera
  • MotionCanvas
Guides
  • Accessibility
  • Reduce bundle size
  • Upgrade guides
Community
  • GitHub
  • Discord

Layout animations

Create layout and shared layout animations with React and Framer Motion.

CSS layouts are difficult and expensive to animate.

Animating a style like height between 100px and 500px is conceptually straightforward, but suffers from poor performance because we're triggering the browser layout systems every animation frame.

Sometimes it doesn't even make sense. What does it actually mean to animate justify-content between flex-start and flex-end?

Framer Motion can animate between any CSS layout by using performant transforms instead of the layout system.

For example, this component is animated by switching justify-content between flex-start and flex-end.

To enable Framer Motion's layout animations, we simply set the layout prop of a motion component.

<motion.div layout />

Any layout change that happens as the result of a re-render will be animated. That could be any combination of:

  • Reordering of a list.
  • A style set on the component itself, for example a change in width or position.
  • A change in the parent's layout, e.g. flexbox or grid.
  • Or any other change in the component's layout.

#Scale correction

All layout animations are performed using the transform property, resulting in smooth framerates.

Animating layout using transforms can sometimes visually distort children. To correct this distortion, the first child elements of the element can also be given layout property.

Try switching this component between layouts, with and without setting layout on the pink dot:

Transforms can also distort boxShadow and borderRadius. The motion component will automatically correct this distortion on both props, as long as they're set as motion values.

If you're not animating these values, the easiest way to do this is to set them via style.

<motion.div layout style={{ borderRadius: 20 }} />

#Customising layout animations

Layout animations can be customised using the transition property.

<motion.div layout transition={{ duration: 0.3 }} />

If you want to set a transition specifically for only the layout animation, you can specify a specific layout transition.

<motion.div
layout
animate={{ opacity: 0.5 }}
transition={{
opacity: { ease: "linear" },
layout: { duration: 0.3 }
}}
/>

#Animating within scroll containers

To animate layout correctly within scrollable elements, these elements must be given the layoutScroll prop.

<motion.div
layoutScroll
style={{ overflow: "scroll" }}
/>

This lets Framer Motion account for this element's scroll offset when measuring children.

#Coordinating layout animations

Layout animations are triggered when a component re-renders and its layout has changed.

function Accordion() {
const [isOpen, setOpen] = useState(false)
return (
<motion.div
layout
style={{ height: isOpen ? "100px" : "500px" }}
onClick={() => setOpen(!isOpen)}
/>
)
}

But what happens when we have two or more components that don't re-render at the same time, but do affect each other's layout?

function List() {
return (
<>
<Accordion />
<Accordion />
</>
)
}

When one re-renders, the other won't be able to detect changes to its layout.

We can synchronise layout changes across multiple components by wrapping them in the LayoutGroup component.

import { LayoutGroup } from "framer-motion"
function List() {
return (
<LayoutGroup>
<Accordion />
<Accordion />
</LayoutGroup>
)
}

Now, when layout changes are detected in one grouped component, layout animations will happen across all of them. Without any extra re-renders.

#Shared layout animations

When a new component is added that has a layoutId prop that matches an existing component, it will automatically animate out from the old component.

isSelected && <motion.div layoutId="underline" />

If the old component is still mounted when the new component enters, they will automatically crossfade from the old to the new.

#Troubleshooting

#The component isn't animating

Ensure the component is not set to display: inline, as browsers don't apply transform to these elements.

#SVG layout animations are broken

SVG components aren't currently supported with layout animations. SVGs don't have layout systems so it's recommended to directly animate their attributes like cx etc.

#Skew transforms aren't taking effect

skew transforms are not currently compatible with layout animations.

#The content stretches undesirably

This is a natural side-effect of animating width and height with scale. Some elements, like those containing changing between different aspect ratios (commonly text elements), might be better animated with layout="position", which only animates the position of the element.

#Border radius or box shadows are behaving strangely

Animating scale is performant but can distort some styles like border-radius and box-shadow.

Framer Motion automatically corrects for this scale distortion but this correction is limited to border-radius defined as pixels or percent, and a single box-shadow.

#Sticky element isn't animating as expected

Elements with position: sticky are difficult to animate as they flip between two coordinate spaces (page-relative and viewport-relative) without any way of detecting which should be used.

If children of the position: sticky element are not animating as expected, it's possible to add layout layoutRoot props to the sticky element. This will perform all the layout calculations and an instant layout animation on the sticky element and all children will perform their layout animations relative to it.

<motion.header layout layoutRoot style={{ position: "sticky" }}>
<motion.h1 layout />
</motion.header>

#Item is performing unwanted layout animations as surrounding content changes

By adding layout layoutRoot to a parent, children will perform all layout animations relative to the parent. See example.

<motion.div className="switch-container" layout layoutRoot>
<motion.div className="switch-handle" layout />
</motion.div>
PreviousAnimationNextGestures
On this page
  • Scale correction
  • Customising layout animations
  • Animating within scroll containers
  • Coordinating layout animations
  • Shared layout animations
  • Troubleshooting

Copyright © 2022 Framer B.V.

  • Security
  • Terms of Service
  • Privacy Statement