Edit Page

#Animation

How to animate in Framer Motion.

Animations in Framer Motion are controlled via the motion component's flexible animate property.

It can be used in a number of ways, scaling to the complexity of your needs.

In this guide, we'll explore each of them.

import { motion } from "framer-motion"

export const MyComponent = () => (
  <motion.div
    animate={{ rotate: 360 }}
    transition={{ duration: 2 }}
  />
)

#Target object

For simple animations, we can set values directly on the animate prop.

<motion.div animate={{ x: 100 }} />

When any value in animate changes, the component will automatically animate to the updated target.

#Transitions

By default, Motion will create an appropriate animation for a snappy transition based on the types of value being animated. For instance, physical properties like x or scale will be animated via a spring simulation. Whereas values like opacity or color will be animated with a tween.

However, you can set different types of animation by passing a Transition to the transition prop.

<motion.div
  animate={{ x: 100 }}
  transition={{ ease: "easeOut", duration: 2 }}
/>

#Mount animations

When a component mounts, it'll automatically animate to the values in animate if they're different from those defined in style or initial. You can set the initial prop to false to use the value in animate as the component's mounted state to disable mount animations.

<motion.div animate={{ x: 100 }} initial={false} />

This will also work with server-side rendering.

#Keyframes

Values in animate can also be set as a series of keyframes. This will animate through each value in sequence.

<motion.feGaussianBlur
  animate={{ stdDeviation: [0, 5, 0] }}
/>

By default, a keyframes animation will start with the first item in the array. To use the current value instead, null can be passed as a placeholder. This way, if a keyframes animation starts while the value is currently animating, the transition will be more natural.

<motion.circle cx={500} animate={{ cx: [null, 100] }} />

Each of these keyframes will be spaced equidistantly throughout the animation, but you can override this by setting a times prop on the transition prop.

This is an array of the same length as the animation target with numbers between 0 and 1 that define when in the animation each keyframe should be hit.

<motion.circle
  cx={500}
  animate={{ cx: [null, 100, 200] }}
  transition={{ duration: 3, times: [0, 0.2, 1] }}
/>

#Variants

Target objects are useful for simple, single-component animations. But sometimes we want to create animations that propagate throughout the DOM, and orchestrate those animations in a declarative way. We can do so with variants.

Variants are sets of pre-defined target objects.

const variants = {
  visible: { opacity: 1 },
  hidden: { opacity: 0 },
}

They're passed into motion components via the variants prop.

<motion.div variants={variants} />

These variants can be referred to by label, wherever you can set an animation target.

<motion.div
  initial="hidden"
  animate="visible"
  variants={variants}
/>

#Propagation

If a motion component has children, changes in variant will flow down through the component hierarchy. These changes in variant will flow down until a child component defines its own animate property.

const list = {
  visible: { opacity: 1 },
  hidden: { opacity: 0 },
}

const item = {
  visible: { opacity: 1, x: 0 },
  hidden: { opacity: 0, x: -100 },
}

return (
  <motion.ul
    initial="hidden"
    animate="visible"
    variants={list}
  >
    <motion.li variants={item} />
    <motion.li variants={item} />
    <motion.li variants={item} />
  </motion.ul>
)

#Orchestration

By default, all these animations will start simultaneously. But by using variants, we gain access to extra transition props like when, delayChildren, and staggerChildren that can let parents orchestrate the execution of child animations.

const list = {
  visible: {
    opacity: 1,
    transition: {
      when: "beforeChildren",
      staggerChildren: 0.3,
    },
  },
  hidden: {
    opacity: 0,
    transition: {
      when: "afterChildren",
    },
  },
}

#Dynamic variants

Each variant can be defined as a function that resolves when a variant is accessed. These variant functions are provided a single argument, which can be set in a component's custom prop.

const variants = {
  visible: i => ({
    opacity: 1,
    transition: {
      delay: i * 0.3,
    },
  }),
  hidden: { opacity: 0 },
}

return items.map((item, i) => (
  <motion.li
    custom={i}
    animate="visible"
    variants={variants}
  />
))

#Animation controls

Declarative animations are ideal for most UI interactions. But sometimes we need to orchestrate more complex sequences.

The useAnimation hook can be used to create a set of imperative AnimationControls with a start and stop method. These controls can be passed to one or more motion components via the animate prop.

const MyComponent = () => {
  const controls = useAnimation()

  return <motion.div animate={controls} />
}

#Starting an animation

Animations can be started with the controls.start method.

controls.start({
  x: "100%",
  backgroundColor: "#f00",
  transition: { duration: 3 },
})

start accepts either a TargetAndTransition or, if the component(s) it's provided to has a variants property set, a variant label.

controls.start("hidden")

#Sequencing

start returns a Promise, so it can be used to sequence animations using await or then.

Different controls can be sequenced together, and these sequences can be composed into functions that themselves can then be sequenced.

const sequence = async () => {
  await menuControls.start({ x: 0 })
  return await itemControls.start({ opacity: 1 })
}

#Dynamic start

start can also accept a function that can dynamically start each component and the controls are bound to with a different animation definition.

Custom data can be sent to this function via the component's custom prop.

const controls = useAnimation()

useEffect(() => {
  controls.start(i => ({
    opacity: 0,
    x: 100,
    transition: { delay: i * 0.3 },
  }))
}, [])

return (
  <ul>
    <motion.li custom={0} animate={controls} />
    <motion.li custom={1} animate={controls} />
    <motion.li custom={2} animate={controls} />
  </ul>
)

#Layout animations

A motion component can automatically animate between different layouts that occur as result of a re-render by setting the layout prop to true.

<motion.div layout />

Any layout change 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.

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

Open sandbox

#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:

<motion.div layout>
  <motion.div layout />
</motion.div>
Open sandbox

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 initial.

<motion.div initial={{ borderRadius: 20 }} />
Note: In a future release it'll also be possible to set these styles via `style` and they'll be automatically corrected.

#Customising layout animations

Layout animations can be customised using the transition property.

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

Each axis can be animated seperately by naming layoutX and layoutY transitions.

Currently, layoutX and layoutY will animate the position and size of an axis simultaneously. In a future release it'll be possible to configure these separately.

<motion.div
  layout
  transition={{
    layoutX: { duration: 0.3 },
    layoutY: { delay: 0.2, duration: 0.3 },
  }}
/>

#Troubleshooting

#The component's layout isn't animating

Ensure the component is set to display: block or display: inline-block, as transform has no effect on display: inline components.

#SVG layout animations are broken

SVG components aren't currently supported with layout animations. This will be fixed in a future release.

#Rotation and skew transforms aren't taking effect

rotate and skew transforms are not currently compatible with layout animations.

#Shared layout animations

Wrapping components with the AnimateSharedLayout component enables you to perform layout animations:

  • Synced across a set of components that don't otherwise share state.
  • Between different components with a common layoutId as they're added/removed.
import { AnimateSharedLayout } from "framer-motion"

When one layout component in an AnimateSharedLayout changes layout, all other layout components check their own layouts and animate accordingly.

Open sandbox
function List({ items, selectedId }) {
  /**
   * Local state changes in Item that affect layout will
   * also affect the parent motion.ul
   */
  return (
    <AnimateSharedLayout>
      <motion.ul layout>
        {items.map(item => (
          <Item {...item} />
        ))}
      </motion.ul>
    </AnimateSharedLayout>
  )
}

When a component with a layoutId prop is removed from one part of the tree and a new component with the same layoutId is added elsewhere, the new component will automatically animate from the old component's position.

Open sandbox
function List({ items, selectedId }) {
  return (
    <AnimateSharedLayout>
      {items.map(item => (
        <li>
          {item.title}
          {item.id === selectedId && (
            <motion.div layoutId="underline" />
          )}
        </li>
      ))}
    </AnimateSharedLayout>
  )
}

The new component will also automatically inherit the current state of any animating values on the old component, and animate from those on entry.

<motion.div
  layoutId="underline"
  animate={{ backgroundColor: item.color }}
/>

All AnimateSharedLayout options can be found in the AnimateSharedLayout docs.