Presence
Track mount/unmount state for animation purposes with animation-aware exit handling.
Presence
The Presence component wraps content and tracks whether it should be visible or hidden. It delays unmounting while exit animations play, ensuring CSS animations complete before the element is removed from the DOM.
Presence is essential for exit animations. It gives your component time to animate out before unmounting, preventing the "flash away" effect.
Features
- Tracks
presentboolean state - Delays unmounting until exit animations finish
- Detects CSS animations via computed styles
- Supports render function children for animation state control
- Works with any CSS animation library (CSS animations, Framer Motion, etc.)
Usage
import { Presence } from '@loke/ui/presence';
export default function Example() {
const [isOpen, setIsOpen] = React.useState(true);
return (
<div>
<button onClick={() => setIsOpen(!isOpen)}>
Toggle
</button>
<Presence present={isOpen}>
<div className="animate-in animate-out fade-in fade-out">
Content with exit animation
</div>
</Presence>
</div>
);
}Props
Prop
Type
Examples
This content is managed by Presence. It will be unmounted from the DOM when hidden (after any exit animations complete).
Content is: mounted and visible
Basic animation
<Presence present={isOpen}>
<div className="transition-opacity duration-200" style={{ opacity: isOpen ? 1 : 0 }}>
Content that animates in/out
</div>
</Presence>Render function for animation state
<Presence present={isOpen}>
{({ present }) => (
<div
className={present ? 'opacity-100' : 'opacity-0'}
style={{ transition: 'opacity 200ms' }}
>
Render function lets you control animation differently
</div>
)}
</Presence>With CSS animations
<Presence present={isOpen}>
<div
className={isOpen ? 'animate-fade-in' : 'animate-fade-out'}
style={{
animation: isOpen
? 'fadeIn 200ms ease-in'
: 'fadeOut 200ms ease-out forwards',
}}
>
CSS animation content
</div>
</Presence>How it works
- When
presentchanges fromtruetofalse, Presence checks for exit animations on the element - If an animation exists, it waits for the animation to finish before unmounting
- If no animation exists (or
display: none), unmounting happens immediately - The render function (if provided) receives
presentstate for coordinating animations
Accessibility
Presence itself has no accessibility implications; it's purely structural. Ensure wrapped content:
- Remains keyboard-navigable while animating
- Uses
aria-hiddenif content is being hidden - Maintains focus management when content unmounts
Best practices
- Always define exit animations when using Presence; otherwise it's unnecessary
- Combine with
DismissableLayerandFocusScopefor complete modals - Keep animations short (200–300ms) to avoid UX delays
- Test that animations complete before unmounting in different browsers