Imperative animation hooks and utilities for programmatic control over animations, including scoped animations and animation controls.
Creates a scoped animation function that can target elements within a specific scope.
/**
* Creates a scoped animation function for imperative animations
* @returns Tuple of [scope ref, animate function]
*/
function useAnimate<T extends Element = any>(): [
scope: AnimationScope<T>,
animate: (
target: string | Element,
keyframes: Record<string, any> | Record<string, any>[],
options?: AnimationOptions
) => AnimationPlaybackControls
];
interface AnimationScope<T extends Element = any> extends React.RefObject<T> {
/**
* Animation scope reference for targeting elements
*/
}
interface AnimationOptions {
/**
* Animation duration in seconds
*/
duration?: number;
/**
* Delay before animation starts in seconds
*/
delay?: number;
/**
* Easing function or array of easing functions
*/
ease?: Easing | Easing[];
/**
* Number of times to repeat animation
*/
repeat?: number;
/**
* Type of repeat: "loop" or "reverse"
*/
repeatType?: "loop" | "reverse" | "mirror";
/**
* Delay between repeats in seconds
*/
repeatDelay?: number;
/**
* Animation type
*/
type?: "tween" | "spring" | "keyframes" | "inertia";
/**
* Spring configuration (when type is "spring")
*/
bounce?: number;
damping?: number;
mass?: number;
stiffness?: number;
velocity?: number;
restSpeed?: number;
restDelta?: number;
}
interface AnimationPlaybackControls {
/**
* Stop the animation
*/
stop: () => void;
/**
* Pause the animation
*/
pause: () => void;
/**
* Resume the animation
*/
play: () => void;
/**
* Reverse the animation direction
*/
reverse: () => void;
/**
* Cancel the animation and reset to initial state
*/
cancel: () => void;
/**
* Complete the animation immediately
*/
complete: () => void;
/**
* Promise that resolves when animation completes
*/
then: (
onResolve?: () => void,
onReject?: (error: any) => void
) => Promise<void>;
}Usage Examples:
import { useAnimate } from "framer-motion";
function AnimatedComponent() {
const [scope, animate] = useAnimate();
const handleClick = async () => {
// Animate multiple elements in sequence
await animate(".card", { x: 100 }, { duration: 0.5 });
await animate(".title", { opacity: 0.5 }, { duration: 0.3 });
// Animate multiple properties
await animate(
".button",
{
scale: [1, 1.2, 1],
rotate: [0, 180, 360],
backgroundColor: ["#ff0000", "#00ff00", "#0000ff"]
},
{ duration: 1, ease: "easeInOut" }
);
};
return (
<div ref={scope}>
<div className="card">Card content</div>
<h1 className="title">Title</h1>
<button className="button" onClick={handleClick}>
Animate
</button>
</div>
);
}Creates animation controls that can be passed to motion components for imperative control.
/**
* Creates animation controls for imperative animation control
* @returns Animation controls instance
*/
function useAnimationControls(): LegacyAnimationControls;
/**
* Alias for useAnimationControls
*/
function useAnimation(): LegacyAnimationControls;
interface LegacyAnimationControls {
/**
* Start an animation
* @param definition - Animation definition or variant name
* @returns Promise that resolves when animation completes
*/
start(definition?: AnimationDefinition): Promise<any>;
/**
* Stop all running animations
*/
stop(): void;
/**
* Mount the controls (called automatically by motion components)
* @returns Cleanup function
*/
mount(): () => void;
}
type AnimationDefinition = string | {
[key: string]: any;
} | ((custom?: any) => {
[key: string]: any;
});Usage Examples:
import { motion, useAnimationControls } from "framer-motion";
function ControlledAnimation() {
const controls = useAnimationControls();
const variants = {
hidden: { opacity: 0, scale: 0.8 },
visible: { opacity: 1, scale: 1 },
pulse: {
scale: [1, 1.1, 1],
transition: { duration: 0.5, repeat: Infinity }
}
};
return (
<div>
<motion.div
animate={controls}
variants={variants}
initial="hidden"
>
Controlled animation
</motion.div>
<button onClick={() => controls.start("visible")}>
Show
</button>
<button onClick={() => controls.start("pulse")}>
Pulse
</button>
<button onClick={() => controls.stop()}>
Stop
</button>
</div>
);
}Utility for creating animation controls outside of React components.
/**
* Creates animation controls instance
* @returns New animation controls
*/
function animationControls(): LegacyAnimationControls;Minimal animation hook for WAAPI-based animations with smaller bundle size.
/**
* Minimal animation hook using Web Animations API
* @returns Animation function for WAAPI animations
*/
function useAnimateMini(): (
target: string | Element,
keyframes: Record<string, any> | Record<string, any>[],
options?: AnimationOptions
) => Animation;Usage Example:
import { useAnimateMini } from "framer-motion/mini";
function MiniAnimation() {
const animate = useAnimateMini();
const handleClick = () => {
animate(
".element",
{ transform: ["translateX(0px)", "translateX(100px)"] },
{ duration: 500, easing: "ease-out" }
);
};
return (
<div>
<div className="element">Element to animate</div>
<button onClick={handleClick}>Animate</button>
</div>
);
}Creating complex animation sequences with timing control:
// Sequential animations
async function sequence() {
await animate(".first", { x: 100 }, { duration: 0.5 });
await animate(".second", { y: 100 }, { duration: 0.5 });
await animate(".third", { rotate: 180 }, { duration: 0.5 });
}
// Parallel animations
function parallel() {
animate(".first", { x: 100 }, { duration: 0.5 });
animate(".second", { y: 100 }, { duration: 0.5, delay: 0.1 });
animate(".third", { rotate: 180 }, { duration: 0.5, delay: 0.2 });
}
// Staggered animations
function stagger() {
const elements = document.querySelectorAll(".item");
elements.forEach((element, index) => {
animate(
element,
{ opacity: 1, y: 0 },
{
duration: 0.5,
delay: index * 0.1,
ease: "easeOut"
}
);
});
}Dynamic Animation Definitions:
const controls = useAnimationControls();
// Function-based animation definition
controls.start((custom) => ({
x: custom.targetX,
y: custom.targetY,
transition: { duration: custom.duration }
}));
// Conditional animations
controls.start(isVisible ? "visible" : "hidden");
// Chained animations
controls.start("slideIn")
.then(() => controls.start("fadeIn"))
.then(() => controls.start("bounce"));Coordinated Multi-Element Animation:
function CoordinatedAnimation() {
const [scope, animate] = useAnimate();
const animateSequence = async () => {
// Animate multiple elements with precise timing
const timeline = [
[".header", { opacity: 1, y: 0 }, { duration: 0.5 }],
[".content", { opacity: 1, x: 0 }, { duration: 0.6, delay: 0.2 }],
[".footer", { opacity: 1, scale: 1 }, { duration: 0.4, delay: 0.4 }]
];
// Run animations in parallel with different delays
timeline.forEach(([target, keyframes, options]) => {
animate(target, keyframes, options);
});
};
return (
<div ref={scope}>
<div className="header" style={{ opacity: 0, y: -20 }}>Header</div>
<div className="content" style={{ opacity: 0, x: -20 }}>Content</div>
<div className="footer" style={{ opacity: 0, scale: 0.8 }}>Footer</div>
<button onClick={animateSequence}>Animate All</button>
</div>
);
}