Loading...
Loading...
Generate React Native Reanimated animation code for React Native apps. Use when user asks to create, implement, or add animations in React Native — including transitions, gestures, scroll-linked effects, layout animations, layout transitions, entering/exiting animations, CSS animations, CSS transitions, shared element transitions, color animations, parallax, fade, slide, scale, spring, timing, decay, keyframes, worklets, accordion, bottom sheet, flip card, collapsing header, or any motion effect using react-native-reanimated. Also use for integrating react-native-gesture-handler with Reanimated, understanding worklets, animating between screens, testing animations with Jest, or checking which properties are animatable.
npx skill4agent add estevg/skills creating-reanimated-animationspackage.jsonreact-native-workletsnpm install react-native-reanimated react-native-worklets'react-native-worklets/plugin'npx expo prebuildnpm install react-native-reanimated'react-native-reanimated/plugin'import Animated, { useSharedValue, useAnimatedStyle, withTiming } from 'react-native-reanimated';
function Component() {
// 1. Create shared value (lives on UI thread)
const offset = useSharedValue(0);
// 2. Bind to style
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ translateX: offset.value }],
}));
// 3. Trigger animation
const handlePress = () => {
offset.value = withTiming(200, { duration: 500 });
};
return (
<Animated.View style={[styles.box, animatedStyle]}>
<Pressable onPress={handlePress}><Text>Move</Text></Pressable>
</Animated.View>
);
}| User wants | Function | Key params |
|---|---|---|
| Smooth fixed-duration transition | | Default: 300ms, |
| Natural bouncy feel | | Default: mass=1, damping=10, stiffness=100 |
| Momentum after fling | | Needs initial velocity from gesture |
| Clamp spring range | | v4: limits spring overshoot |
| Loop/pulse/shake | | |
| Multi-step choreography | | Runs in order |
| Delayed start | | Delays any animation |
dampingstiffnessmassconst opacity = useSharedValue(0);
const style = useAnimatedStyle(() => ({ opacity: opacity.value }));
// Show: opacity.value = withTiming(1);
// Hide: opacity.value = withTiming(0);const translateX = useSharedValue(-SCREEN_WIDTH);
const style = useAnimatedStyle(() => ({
transform: [{ translateX: translateX.value }],
}));
// Enter: translateX.value = withSpring(0);const scale = useSharedValue(1);
const style = useAnimatedStyle(() => ({
transform: [{ scale: scale.value }],
}));
// In: scale.value = withSpring(0.95);
// Out: scale.value = withSpring(1);import { interpolate, Extrapolation } from 'react-native-reanimated';
const style = useAnimatedStyle(() => ({
opacity: interpolate(scrollY.value, [0, 100], [1, 0], Extrapolation.CLAMP),
transform: [{
scale: interpolate(scrollY.value, [0, 100], [1, 0.8], Extrapolation.CLAMP),
}],
}));const scrollY = useSharedValue(0);
const scrollHandler = useAnimatedScrollHandler({
onScroll: (event) => { scrollY.value = event.contentOffset.y; },
});
<Animated.ScrollView onScroll={scrollHandler}>{children}</Animated.ScrollView>useScrollOffsetuseScrollViewOffsetconst ref = useAnimatedRef<Animated.ScrollView>();
const scrollOffset = useScrollOffset(ref); // auto-tracks scroll position
<Animated.ScrollView ref={ref}>{children}</Animated.ScrollView>import Animated, { FadeIn, SlideInRight, SlideOutLeft } from 'react-native-reanimated';
<Animated.View entering={FadeIn.duration(400).delay(100)} exiting={SlideOutLeft.duration(300)}>
{content}
</Animated.View>FadeIn/OutSlideIn/OutLeft/Right/Up/DownZoomIn/OutBounceIn/OutFlipInEasyX/YLightSpeedIn/OutPinwheelIn/OutRollIn/OutRotateIn/OutStretchIn/Out.duration(ms).delay(ms).springify().damping(n).stiffness(n).randomDelay().reduceMotion().withCallback()import { LinearTransition, SequencedTransition } from 'react-native-reanimated';
<Animated.View layout={LinearTransition.springify().damping(15)} />LinearTransitionSequencedTransitionFadingTransitionJumpingTransitionCurvedTransitionEntryExitTransitionimport { Keyframe } from 'react-native-reanimated';
const enteringAnimation = new Keyframe({
0: { opacity: 0, transform: [{ translateY: -50 }] },
50: { opacity: 0.5, transform: [{ translateY: -25 }], easing: Easing.out(Easing.quad) },
100: { opacity: 1, transform: [{ translateY: 0 }] },
}).duration(500);
<Animated.View entering={enteringAnimation}>{content}</Animated.View>references/layout-animations.md<Animated.View style={{
animationName: {
from: { opacity: 0, transform: [{ translateY: -20 }] },
to: { opacity: 1, transform: [{ translateY: 0 }] },
},
animationDuration: '500ms',
animationTimingFunction: 'ease-out',
}} />animationNameanimationDurationanimationDelayanimationIterationCountanimationDirectionanimationFillModeanimationPlayStateanimationTimingFunction<Animated.View style={{
width: isExpanded ? 200 : 100,
transitionProperty: 'width',
transitionDuration: '300ms',
transitionTimingFunction: 'ease-in-out',
}} />transitionProperty: ['width', 'opacity', 'transform']transitionDuration: ['300ms', '200ms', '500ms']references/css-animations-detailed.md// Screen A
<Animated.Image sharedTransitionTag={`photo-${id}`} source={...} />
// Screen B (same tag)
<Animated.Image sharedTransitionTag={`photo-${id}`} source={...} />@react-navigation/native-stackreferences/shared-element-transitions.mdreferences/gesture-patterns.mdreact-native-gesture-handler<GestureHandlerRootView>references/api-reference.md'worklet'runOnJSrunOnUIuseAnimatedReactionuseFrameCallbackmeasuredispatchCommandreferences/worklets-advanced.mdreferences/component-patterns.mdinterpolateColorreferences/testing-and-properties.mdAnimated.View/Text/Image/ScrollView/FlatListrunOnJSrunOnUIscheduleOnRNscheduleOnUIreact-native-workletsrunOnJS(fn)()Extrapolation.CLAMPinterpolatewithSequence(withTiming(1.2, {duration: 100}), withSpring(1))useReducedMotion()useAnimatedKeyboardreact-native-keyboard-controllerBased on react-native-reanimated by Software Mansion (MIT License)