useAnimate
provides a way of using the animate
function that is scoped to the elements within your component.
This allows you to use manual animation controls, timelines, selectors scoped to your component, and automatic cleanup.
It provides a scope
ref, and an animate
function where every DOM selector is scoped to this ref.
function Component() {
const [scope, animate] = useAnimate()
useEffect(() => {
animate("li", { opacity: 1 })
})
return <ul ref={scope}>{children}</ul>
}
Additionally, when the component calling useAnimate
is removed, all animations started with its animate
function will be cleaned up automatically.
Import from Motion:
import { useAnimate } from "motion/react-mini"
import { useAnimate } from "motion/react"
useAnimate
returns two arguments, a scope
ref and an animate
function.
function Component() {
const [scope, animate] = useAnimate()
This scope
ref must be passed to either a regular HTML/SVG element or a motion
component.
function Component({ children }) {
const [scope, animate] = useAnimate()
return <ul ref={scope}>{children}</ul>
}
This scoped animate
function can now be used in effects and event handlers to animate elements.
We can either use the scoped element directly:
animate(scope.current, { opacity: 1 }, { duration: 1 })
Or by passing it a selector:
animate("li", { backgroundColor: "#000" }, { ease: "linear" })
This selector is "li"
, but we're not selecting all li
elements on the page, only those that are a child of the scoped element.
Animations can be triggered when the scope scrolls into view by combining useAnimate
with useInView
.
import { useAnimate, useInView } from "motion/react"
function Component() {
const [scope, animate] = useAnimate()
const isInView = useInView(scope)
useEffect(() => {
if (isInView) {
animate(scope.current, { opacity: 1 })
}
}, [isInView])
return (
<ul ref={scope}>
<li />
<li />
<li />
</ul>
)
}
It's possible to compose your own exit animations when a component is removed using useAnimate
in conjunction with usePresence
.
import { useAnimate, usePresence } from "framer-motion"
function Component() {
const [isPresent, safeToRemove] = usePresence()
const [scope, animate] = useAnimate()
useEffect(() => {
if (isPresent) {
const enterAnimation = async () => {
await animate(scope.current, { opacity: 1 })
await animate("li", { opacity: 1, x: 0 })
}
enterAnimation()
} else {
const exitAnimation = async () => {
await animate("li", { opacity: 0, x: -100 })
await animate(scope.current, { opacity: 0 })
safeToRemove()
}
exitAnimation()
}
}, [isPresent])
return (
<ul ref={scope}>
<li />
<li />
<li />
</ul>
)
}
This component can now be conditionally rendered as a child of AnimatePresence
.
<AnimatePresence>
{show ? <Component key="dialog" /> : null}
</AnimatePresence>