useAnimatePresence
▸ useAnimatePresence(props
: AnimatePresenceProps
): AnimatePresenceReturn
useAnimatePresence
useAnimatePresence
helps you animate components that are added or removed from the DOM.
Great for things like modals, tooltips, dropdowns, etc.
Demo
Usage
Basic example
By default all you need to provide is a boolean that determines whether the element should be visible or not. The hook will return a render
boolean that you can use to conditionally render the element.
It will also return a animate
boolean, which is a shorthand for state === OPENING || state === OPENED
that you can use to conditionally apply your animation.
import { useState } from "react"
import { useAnimatePresence } from "@wethegit/react-hooks"
function MyComponent() {
const [isVisible, setIsVisible] = useState(false)
const { render, animate } = useAnimatePresence({
isVisible,
duration: 500,
})
return (
<>
<button onClick={() => setIsVisible((cur) => !cur)}>
{render ? "Hide" : "Show"}
</button>
{render && (
<div className={`my-component ${animate && "is-visible"}`}>
Hey! I animate in and out!
</div>
)}
</>
)
}
And then in your styles:
.my-component {
opacity: 0;
transition: opacity 500ms ease-out;
&.is-visible {
opacity: 1;
}
}
Custom animation
The hook will return a currentDuration
number that you can use to set the duration of your animation. This is optional, but very useful so you don't have to hardcode the duration in your styles.
It will also return a state
enum that you can use to control the animation in every step.
import { useAnimatePresence, AnimatePresenceStates } from "@local/hooks"
function MyComponent() {
// ... shortened for brevity
const { render, animate, currentDuration } = useAnimatePresence({
state,
isVisible,
duration: {
// different durations for enter and exit
enter: 1000,
exit: 500,
},
})
// you can control the animation at every step
let className
if (state === AnimatePresenceStates.ENTERING) {
className = "is-entering"
} else if (state === AnimatePresenceStates.ENTERED) {
className = "is-entered"
} else if (state === AnimatePresenceStates.EXITING) {
className = "is-exiting"
} else if (state === AnimatePresenceStates.EXITED) {
className = "is-exited"
}
return (
<>
{/* render it the same way, the hook takes care of updating the duration */}
{render && (
<div
className={`my-component ${className}`}
style={{
"--duration": `${currentDuration}ms`,
}}
>
Hey there!
</div>
)}
</>
)
}
And then in your styles:
.my-component {
transition: all var(--transition-duration) ease-out;
&.is-entering {
opacity: 1;
transform: scale(1.5);
}
&.is-entered {
opacity: 1;
transform: scale(1);
}
&.is-exiting,
&.is-exited {
opacity: 0;
transform: scale(0) rotate(360deg);
transition-timing-function: ease-in;
}
}
Accessibility
You can use the useUserPrefs
hook to help you with accessibility by disabling animations when the user prefers reduced motion.
import { useAnimatePresence, useUserPrefs } from "@local/hooks"
function MyComponent() {
// use the prefersReducedMotion preference to disable animations
const { prefersReducedMotion } = useUserPrefs()
const { render, animate } = useAnimatePresence({
isVisible,
// disable animations if the user prefers reduced motion
duration: prefersReducedMotion ? 0 : 300,
})
}
Don't animate on mount
Set initial
to true
to have the element start out visible.
const { render, animate } = useAnimatePresence({
initial: true,
isVisible,
})
Parameters
Name | Type |
---|---|
props | AnimatePresenceProps |
AnimatePresenceProps
duration
• Optional
duration: number
| { enter
: number
; exit
: number
}
Duration in miliseconds or object with enter and exit duration in miliseconds
Default Value
= 300
initial
• Optional
initial: boolean
Initial state of the animation, if true
the component won't animate in on render
Default Value
false
isVisible
• isVisible: boolean
Visibility of the component
Returns
AnimatePresenceReturn
render
• render: boolean
render
is an shorthand for animating the component in and out
currentDuration
• currentDuration: number
Duration of the current animation
render
• render: boolean
Render your component only if render
is true
state
• state: AnimatePresenceState
export enum AnimatePresenceState {
ENTERED = "entered",
EXITED = "exited",
EXITING = "exiting",
ENTERING = "entering",
MOUNTED = "mounted",
}
Current state of the animation. Use it for full control of the animation in all states