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.

On this page

Copyright © 2022 Framer B.V.

Cookies

Security

Terms of Service

Privacy Statement

Copyright © 2022 Framer B.V.

Cookies

Security

Terms of Service

Privacy Statement

Copyright © 2022 Framer B.V.

Cookies

Security

Terms of Service

Privacy Statement