useAnimate
Create an animate function with scoped selectors and automatic cleanup.
useAnimate
provides a way of using the animate
function that is scoped to the elements within your component.
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(() => { // This "li" selector will only select children // of the element that receives `scope`. animate("li", { opacity: 1 }) }) return <ul ref={scope}>{children}</ul>}
Additionally, when the component calling useAnimate
is removed, all animations started with its returned animate
function will be cleaned up automatically.
#useAnimate()
vs <motion.div />
Declarative animations via motion components
tend to be simpler to write and maintain. However, imperative animations via useAnimate()
are very powerful, allowing:
- Complex animation sequencing
- Playback controls (
pause
/play
etc) - Animate
MotionValue
s directly - Animating "black box" components, where you don't have direct access to rendered components, via DOM selectors
Ultimately which you use depends on your needs and usecase.
#Usage
Import from "framer-motion"
.
import { useAnimate } from "framer-motion"
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.
#Scroll-triggered animations
Animations can be triggered when the scope scrolls into view by using useAnimate
with useInView
.
import { useAnimate, useInView } from "framer-motion"
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> )}
#Exit animations
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>