Skip to main content
This API is deprecated. Use useAnimate() for new projects, which provides a more flexible and modern animation API.
AnimationControls (also called LegacyAnimationControls internally) allows you to manually control animations across multiple Motion components.

Creating Controls

Create controls using the animationControls() function:
function animationControls(): LegacyAnimationControls
In React, use the useAnimation() hook:
function useAnimation(): LegacyAnimationControls

Type Definition

interface LegacyAnimationControls {
  subscribe(visualElement: VisualElement): () => void
  start(definition: AnimationDefinition, transitionOverride?: Transition): Promise<any>
  set(definition: AnimationDefinition): void
  stop(): void
  mount(): () => void
}

Methods

start
(definition, transitionOverride?) => Promise<any>
Start an animation on all subscribed components.Returns a Promise that resolves when all animations complete.Note: Must be called after component mounts (e.g., in useEffect)
set
(definition) => void
Instantly set values without animating.Note: Must be called after component mounts (e.g., in useEffect)
stop
() => void
Stop all animations on subscribed components.
subscribe
(visualElement) => () => void
Subscribe a visual element to these controls. Returns an unsubscribe function.Note: This is called automatically when you pass controls to a component’s animate prop. You typically won’t call this directly.
mount
() => () => void
Mark controls as mounted. Returns a cleanup function.Note: This is called automatically by useAnimation(). You typically won’t call this directly.

Usage in React

Basic Example

import { motion, useAnimation } from "motion/react"
import { useEffect } from "react"

function Component() {
  const controls = useAnimation()
  
  useEffect(() => {
    controls.start({ 
      x: 100,
      transition: { duration: 0.5 }
    })
  }, [])
  
  return <motion.div animate={controls} />
}

Control Multiple Elements

import { motion, useAnimation } from "motion/react"

function Component() {
  const controls = useAnimation()
  
  return (
    <>
      <motion.div animate={controls} />
      <motion.div animate={controls} />
      <motion.div animate={controls} />
      
      <button
        onClick={() => controls.start({ scale: 1.2 })}
      >
        Animate All
      </button>
    </>
  )
}

Using Variants

import { motion, useAnimation } from "motion/react"
import { useEffect } from "react"

const variants = {
  hidden: { opacity: 0, y: 50 },
  visible: { 
    opacity: 1, 
    y: 0,
    transition: { duration: 0.5 }
  }
}

function Component() {
  const controls = useAnimation()
  
  useEffect(() => {
    controls.start("visible")
  }, [])
  
  return (
    <motion.div
      variants={variants}
      initial="hidden"
      animate={controls}
    />
  )
}

Sequence Animations

import { motion, useAnimation } from "motion/react"

function Component() {
  const controls = useAnimation()
  
  async function sequence() {
    await controls.start({ x: 100 })
    await controls.start({ y: 100 })
    await controls.start({ x: 0, y: 0 })
  }
  
  return (
    <>
      <motion.div animate={controls} />
      <button onClick={sequence}>Run Sequence</button>
    </>
  )
}

Dynamic Orchestration

import { motion, useAnimation } from "motion/react"

const listVariants = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: {
      when: "beforeChildren",
      staggerChildren: 0.1
    }
  }
}

const itemVariants = {
  hidden: { opacity: 0, x: -50 },
  visible: { opacity: 1, x: 0 }
}

function List() {
  const listControls = useAnimation()
  
  return (
    <>
      <motion.ul
        variants={listVariants}
        animate={listControls}
      >
        {items.map(item => (
          <motion.li key={item.id} variants={itemVariants">
            {item.text}
          </motion.li>
        ))}
      </motion.ul>
      
      <button onClick={() => listControls.start("visible")">
        Show Items
      </button>
    </>
  )
}

Override Transitions

function Component() {
  const controls = useAnimation()
  
  return (
    <>
      <motion.div
        animate={controls}
        transition={{ type: "spring" }}
      />
      
      <button
        onClick={() => {
          // Override with instant transition
          controls.start(
            { x: 100 },
            { duration: 0 }
          )
        }}
      >
        Jump
      </button>
    </>
  )
}

Set Without Animating

function Component() {
  const controls = useAnimation()
  
  return (
    <>
      <motion.div animate={controls} />
      
      <button onClick={() => controls.set({ x: 100 })">
        Set Position (No Animation)
      </button>
      
      <button onClick={() => controls.start({ x: 0 })">
        Animate Back
      </button>
    </>
  )
}

Usage in Vanilla JS

You can use animationControls() outside of React:
import { animationControls } from "motion"
import { createVisualElement } from "motion-dom"

const controls = animationControls()

// Manually subscribe visual elements
controls.mount()

// Subscribe elements
const unsubscribe = controls.subscribe(visualElement)

// Start animations
controls.start({ x: 100 })

// Cleanup
unsubscribe()

Type Definitions

type AnimationDefinition = 
  | TargetAndTransition
  | VariantLabels
  | TargetResolver

type TargetAndTransition = Target & {
  transition?: Transition
  transitionEnd?: ResolvedValues
}

type VariantLabels = string | string[]

type TargetResolver = (
  custom: any,
  current: ResolvedValues,
  velocity: ResolvedValues
) => TargetAndTransition | string

interface Transition {
  duration?: number
  delay?: number
  ease?: Easing | Easing[]
  type?: "tween" | "spring" | "inertia" | false
  // ... other transition properties
}

Migration to useAnimate

The modern useAnimate() hook provides more flexibility:
import { motion, useAnimation } from "motion/react"
import { useEffect } from "react"

function Component() {
  const controls = useAnimation()
  
  useEffect(() => {
    controls.start({ x: 100 })
  }, [])
  
  return <motion.div animate={controls} />
}
Benefits of useAnimate():
  • Works with regular HTML elements (no motion. wrapper required)
  • Can target child elements with selectors
  • More intuitive API for sequences
  • Better TypeScript support

See Also

Build docs developers (and LLMs) love