plans/tamagui-skill/skills/tamagui/references/animations.md
Tamagui supports multiple animation drivers. Choose based on your platform:
| Driver | Package | Best For |
|---|---|---|
| CSS | @tamagui/animations-css | Web-only apps, smallest bundle |
| React Native | @tamagui/animations-react-native | Native apps, basic animations |
| Reanimated | @tamagui/animations-reanimated | Native apps, best performance |
| Motion | @tamagui/animations-motion | Cross-platform spring physics |
// v5 config - import driver separately
import { defaultConfig } from '@tamagui/config/v5'
import { animations } from '@tamagui/config/v5-css'
// or: '@tamagui/config/v5-motion'
// or: '@tamagui/config/v5-rn'
// or: '@tamagui/config/v5-reanimated'
export const config = createTamagui({
...defaultConfig,
animations,
})
Uses CSS transition strings:
import { createAnimations } from '@tamagui/animations-css'
const animations = createAnimations({
fast: 'ease-in 150ms',
medium: 'ease-in-out 300ms',
slow: 'ease-out 500ms',
bouncy: 'cubic-bezier(0.68, -0.55, 0.265, 1.55) 400ms',
})
Use spring physics config:
import { createAnimations } from '@tamagui/animations-react-native'
const animations = createAnimations({
fast: {
type: 'spring',
damping: 20,
stiffness: 300,
},
medium: {
type: 'spring',
damping: 15,
stiffness: 200,
},
bouncy: {
type: 'spring',
damping: 8,
mass: 0.8,
stiffness: 100,
},
})
<View
animation="medium"
opacity={isVisible ? 1 : 0}
y={isVisible ? 0 : 10}
/>
<View
animation="fast"
enterStyle={{
opacity: 0,
y: -20,
scale: 0.9,
}}
exitStyle={{
opacity: 0,
y: 20,
scale: 0.9,
}}
opacity={1}
y={0}
scale={1}
/>
Exit animations only work inside AnimatePresence:
import { AnimatePresence } from 'tamagui'
<AnimatePresence>
{show && (
<View
key="unique-key" // key is required
animation="medium"
enterStyle={{ opacity: 0 }}
exitStyle={{ opacity: 0 }}
opacity={1}
/>
)}
</AnimatePresence>
Override animation for specific properties:
<View
animation={[
'fast',
{
opacity: { type: 'timing', duration: 500 },
scale: { overshootClamping: true },
},
]}
opacity={1}
scale={1}
/>
Common animatable style properties:
opacityx, y (translateX/Y)scale, scaleX, scaleYrotate, rotateX, rotateY, rotateZwidth, heightbackgroundColorborderColorborderRadiusState-based animations:
<Button
animation="fast"
hoverStyle={{
scale: 1.05,
backgroundColor: '$blue9',
}}
pressStyle={{
scale: 0.95,
backgroundColor: '$blue11',
}}
/>
const AnimatedCard = styled(View, {
animation: 'medium',
variants: {
visible: {
true: {
opacity: 1,
y: 0,
},
false: {
opacity: 0,
y: 20,
},
},
} as const,
})
<AnimatedCard visible={isVisible} />
<View
animation="medium"
enterStyle={{ opacity: 0 }}
opacity={1}
/>
<View
animation="fast"
enterStyle={{ opacity: 0, y: 20 }}
opacity={1}
y={0}
/>
<View
animation="bouncy"
enterStyle={{ opacity: 0, scale: 0.8 }}
opacity={1}
scale={1}
/>
<Dialog.Overlay
animation="fast"
enterStyle={{ opacity: 0 }}
exitStyle={{ opacity: 0 }}
opacity={0.5}
/>
<Dialog.Content
animation={['medium', { opacity: { overshootClamping: true } }]}
enterStyle={{ opacity: 0, y: -20, scale: 0.95 }}
exitStyle={{ opacity: 0, y: 10, scale: 0.98 }}
opacity={1}
y={0}
scale={1}
/>