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>
)

#Automatic (beta)

Note: This feature is currently in beta. You can install the latest Framer Motion 2 beta using npm install framer-motion@beta.

A motion component can automatically animate layout and style changes that occur as a result of a re-render by setting animate to true.

These layout and style changes can be set via CSS applied via className, style, or inherited from a stylesheet.

<motion.div animate />

#Layout animations

Auto-animating components will animate any changes to their layout. These layout changes can be anything: a change in the component's parent's flexbox, CSS grid, or switching between position: absolute and position: fixed.

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

All layout animations are performed using the transform property rather than properties like width or top. This results in smoother animations, because it does not trigger expensive layouts during the animation.

Animating width and height with scaleX and scaleY can sometimes visually distort children. You can correct this by also applying animate to those children if they are motion elements:

<motion.div animate>
  <motion.div animate />
</motion.div>

#Style animations

The styles that will automatically animate are:

  • backgroundColor
  • borderRadius (and individual corners)
  • boxShadow (currently single shadow only)
  • color
  • opacity

#Current limitations

  • Transform: animate doesn't currently automatically animate transform, because the transform prop is currently reserved for performing layout animations.
  • Drag: The layoutTransition prop that animate={true} replaces used to have a hacky way to integrate it with drag that powered the drag-to-reorder demos. The plan is to properly fix integration with drag rather than re-expose this kind of API.
  • Inline components: Components that are set to display: inline aren't compatible with layout animations as they don't accept the CSS transform property. Try using display: inline-block instead.
  • SVG: SVGs are currently not supported, but they will be soon.