code/tamagui.dev/data/docs/core/animate-presence.mdx
AnimatePresence animates direct children before they unmount. It is inspired by and forked from Framer Motion, but works with any animation driver in Tamagui.
To use with @tamagui/core, install and import @tamagui/animate-presence. It's already bundled and exported from tamagui.
Use enterStyle and exitStyle to define how components animate in and out:
import { AnimatePresence, View } from 'tamagui'
export const MyComponent = ({ isVisible }) => (
<AnimatePresence>
{isVisible && (
<View
key="my-square"
transition="bouncy"
backgroundColor="green"
size={50}
enterStyle={{
opacity: 0,
y: 10,
scale: 0.9,
}}
exitStyle={{
opacity: 0,
y: -10,
scale: 0.9,
}}
/>
)}
</AnimatePresence>
)
Wrap one or more Tamagui components with AnimatePresence. The child components will animate on enter and exit.
<Notice theme="blue"> Animated child components must each have a unique key prop so AnimatePresence can track their presence in the tree. </Notice>You can specify different animations for enter (mount) and exit (unmount) transitions. This is useful when you want elements to enter slowly but exit quickly, or vice versa:
import { AnimatePresence, View } from 'tamagui'
export default ({ show }) => (
<AnimatePresence>
{show && (
<View
key="panel"
transition={{ enter: 'lazy', exit: 'quick' }}
enterStyle={{ opacity: 0, y: 20 }}
exitStyle={{ opacity: 0, y: -20 }}
/>
)}
</AnimatePresence>
)
The enter and exit keys accept any animation name from your config. You can also combine them with a default for property changes that happen while the element is mounted:
// enter slowly, exit quickly, property changes use medium speed
<View
transition={{ enter: 'lazy', exit: 'quick', default: 'bouncy' }}
enterStyle={{ opacity: 0 }}
exitStyle={{ opacity: 0 }}
/>
Or use enter/exit with the array syntax to include delay and per-property animations:
// enter with lazy, exit with quick, delay 200ms, x uses its own animation
<View
transition={['bouncy', { enter: 'lazy', exit: 'quick', delay: 200, x: 'slow' }]}
enterStyle={{ opacity: 0, x: -100 }}
exitStyle={{ opacity: 0, x: 100 }}
/>
This works with all four animation drivers (CSS, React Native, Reanimated, Motion).
custom propAnimatePresence takes a custom prop that allows you to update a variant of the animated child before it runs its exit animation. This is useful for animating a child out of the screen in a different direction before it unmounts, as shown in the example above:
import { AnimatePresence } from '@tamagui/animate-presence'
import { ArrowLeft, ArrowRight } from '@tamagui/lucide-icons-2'
import { useState } from 'react'
import { Button, Image, XStack, YStack, styled } from 'tamagui'
const GalleryItem = styled(YStack, {
zIndex: 1,
x: 0,
opacity: 1,
fullscreen: true,
variants: {
// 1 = right, 0 = nowhere, -1 = left
going: {
':number': (going) => ({
enterStyle: {
x: going > 0 ? 1000 : -1000,
opacity: 0,
},
exitStyle: {
zIndex: 0,
x: going < 0 ? 1000 : -1000,
opacity: 0,
},
}),
},
} as const,
})
const photos = [
'https://picsum.photos/500/300',
'https://picsum.photos/501/300',
'https://picsum.photos/502/300',
]
const wrap = (min: number, max: number, v: number) => {
const rangeSize = max - min
return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min
}
export function Demo() {
const [[page, going], setPage] = useState([0, 0])
const imageIndex = wrap(0, photos.length, page)
const paginate = (going: number) => {
setPage([page + going, going])
}
return (
<XStack
overflow="hidden"
backgroundColor="#000"
position="relative"
height={300}
width="100%"
alignItems="center"
>
<AnimatePresence initial={false} custom={{ going }}>
<GalleryItem key={page} transition="slowest" going={going}>
<Image src={photos[imageIndex]} width={500} height={300} />
</GalleryItem>
</AnimatePresence>
<Button
accessibilityLabel="Carousel left"
icon={ArrowLeft}
size="$5"
position="absolute"
left="$4"
circular
elevate
onPress={() => paginate(-1)}
zi={100}
/>
<Button
accessibilityLabel="Carousel right"
icon={ArrowRight}
size="$5"
position="absolute"
right="$4"
circular
elevate
onPress={() => paginate(1)}
zi={100}
/>
</XStack>
)
}
children - One or more Tamagui components with unique key propsinitial - If false, children entering on initial mount won't animate (default: true)custom - Pass data to children's variants for dynamic exit animationsexitBeforeEnter - If true, only one child renders at a time (default: false)onExitComplete - Callback fired when all exiting children have finished animatingWhen used inside AnimatePresence:
enterStyle - Styles to animate from when mountingexitStyle - Styles to animate to when unmountingtransition - Animation configuration (can use { enter, exit } syntax)key - Required unique identifier for AnimatePresence tracking