agentsclimarketplace

Mobile animation reanimated

Skill agents-inc/skills/dist/plugins/mobile-animation-reanimated/skills/mobile-animation-reanimated

React Native Reanimated 4 - shared values, animated styles, spring/timing/decay, layout animations, gesture integration, scroll-driven animations, interpolation, worklets, CSS animationsFrom its SKILL.md

Install
npx -y skills add agents-inc/skills --skill mobile-animation-reanimated

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 20 stars20 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

18.9 KB, ~4.4k tokens by cl100k_base, as published. Nobody here has run it

React Native Reanimated Patterns

Quick Guide: Reanimated 4 is New Architecture only (requires react-native-worklets as a separate dependency). Use useSharedValue + useAnimatedStyle for all animations. Animations run on the UI thread via worklets -- never block the JS thread. Use withSpring (physics-based) or withTiming (duration-based) for transitions, layout animations (entering/exiting) for mount/unmount, and useScrollOffset (renamed from useScrollViewOffset) for scroll-driven animations. Reanimated 4 also introduces CSS animations/transitions as a declarative alternative to the worklet API.


<critical_requirements>

CRITICAL: Before Using This Skill

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

(You MUST use Animated components (Animated.View, Animated.Text, etc.) for any animated styles -- passing animated styles to regular components causes errors)

(You MUST keep static styles in StyleSheet.create and only animate dynamic properties in useAnimatedStyle -- animating static values wastes UI thread resources)

(You MUST NOT mutate shared values inside useAnimatedStyle callbacks -- read only, or you cause infinite loops)

(You MUST use react-native-worklets as a separate dependency in Reanimated 4 -- the worklet Babel plugin moved from react-native-reanimated/plugin to react-native-worklets/plugin)

</critical_requirements>


Auto-detection: Reanimated, react-native-reanimated, useSharedValue, useAnimatedStyle, withSpring, withTiming, withDecay, Animated.View, Animated.Text, Animated.ScrollView, entering, exiting, FadeIn, FadeOut, SlideIn, interpolate, interpolateColor, useScrollOffset, GestureDetector, Gesture.Pan, worklet, layout animation, shared value, energyThreshold, CSS animation, react-native-worklets

When to use:

  • Animating view properties (opacity, transforms, colors) on the UI thread
  • Adding entering/exiting animations when components mount/unmount
  • Building gesture-driven animations (drag, swipe, pinch)
  • Creating scroll-driven header collapse, parallax, or sticky effects
  • Interpolating values across ranges (position to opacity, scroll to scale)
  • Implementing spring physics or timing-based transitions

When NOT to use:

  • Simple boolean show/hide without animation (conditional rendering suffices)
  • Static layouts that never change at runtime
  • Animated.Value from React Native core (use Reanimated's shared values instead)

Key patterns covered:

  • Shared values (useSharedValue) + animated styles (useAnimatedStyle)
  • Animation functions: withTiming, withSpring, withDecay
  • Layout animations: entering/exiting with predefined builders
  • Gesture integration: Gesture.Pan + shared values + withDecay
  • Scroll-driven animations with useScrollOffset
  • Interpolation: interpolate and interpolateColor
  • Worklet functions and the 'worklet' directive
  • CSS animations and transitions (Reanimated 4 declarative API)

Detailed Resources:


<philosophy>

Philosophy

Reanimated runs animations on the UI thread via worklets, keeping the JS thread free for business logic. The core model: shared values are reactive state that bridges JS and UI threads, animated styles derive visual properties from shared values, and animation functions (withSpring, withTiming, withDecay) drive transitions between values.

Reanimated 4 key changes from 3.x:

  • New Architecture only -- drops Legacy Architecture (bridge) support entirely
  • react-native-worklets as a separate package -- Babel plugin moved from react-native-reanimated/plugin to react-native-worklets/plugin
  • CSS animations/transitions -- declarative API for state-driven animations (use CSS for simple state transitions, worklets for gesture/scroll-driven)
  • energyThreshold replaces restDisplacementThreshold/restSpeedThreshold in withSpring
  • useScrollOffset replaces useScrollViewOffset (deprecated alias remains)
  • Threading functions moved to react-native-worklets: runOnJS -> scheduleOnRN, runOnUI -> scheduleOnUI

When to use CSS animations vs worklets:

  • CSS animations/transitions -- state-driven, declarative, less code, better optimizable by Reanimated
  • Worklets -- gesture-driven, scroll-driven, complex orchestration, frame-by-frame control

Backward compatibility: All v2/v3 shared value and animation APIs work unchanged in v4. CSS animations and worklet-based animations work simultaneously and interchangeably.

</philosophy>
<patterns>

Core Patterns

Pattern 1: Shared Values and Animated Styles

The fundamental building blocks. useSharedValue creates reactive state on the UI thread. useAnimatedStyle derives styles that update when shared values change.

import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withTiming,
} from "react-native-reanimated";

const EXPANDED_HEIGHT = 200;
const COLLAPSED_HEIGHT = 60;
const ANIMATION_DURATION = 300;

function CollapsibleCard() {
  const height = useSharedValue(COLLAPSED_HEIGHT);

  const animatedStyle = useAnimatedStyle(() => ({
    height: height.value,
  }));

  const toggle = () => {
    height.value = withTiming(
      height.value === COLLAPSED_HEIGHT ? EXPANDED_HEIGHT : COLLAPSED_HEIGHT,
      { duration: ANIMATION_DURATION }
    );
  };

  return (
    <Pressable onPress={toggle}>
      <Animated.View style={[styles.card, animatedStyle]}>
        <Text>Content</Text>
      </Animated.View>
    </Pressable>
  );
}

Why good: static styles stay in StyleSheet, only dynamic height in useAnimatedStyle, named constants for dimensions and durations, Animated.View receives the animated style

Key rules:

  • Only animate dynamic properties in useAnimatedStyle -- static styles belong in StyleSheet.create
  • Never mutate shared values inside useAnimatedStyle -- it is read-only
  • Always apply animated styles to Animated.* components, not regular View/Text

See examples/core.md for complete examples including React Compiler compatibility (get()/set()).


Pattern 2: withSpring and withTiming

withTiming is duration-based (predictable timing). withSpring is physics-based (natural feel). Choose based on UX intent.

// Physics-based spring (natural, bouncy)
sv.value = withSpring(TARGET, { damping: 100, stiffness: 800 });

// Duration-based spring (controlled timing with spring feel)
sv.value = withSpring(TARGET, { duration: 500, dampingRatio: 0.8 });

// Timing with easing
sv.value = withTiming(TARGET, {
  duration: ANIMATION_DURATION,
  easing: Easing.bezierFn(0.25, 0.1, 0.25, 1),
});

Reanimated 4 spring change: restDisplacementThreshold and restSpeedThreshold are removed. Replaced by energyThreshold (relative to animation, default 6e-9). In most cases, removing the old thresholds is sufficient -- no need to set energyThreshold manually.

Duration gotcha: In v4, actual spring completion time = perceptual duration x 1.5. Divide existing duration values by 1.5 for equivalent behavior when migrating from v3.

See examples/core.md for spring config comparison and withDecay.


Pattern 3: Layout Animations (Entering/Exiting)

Predefined animations for component mount/unmount. Apply to Animated.* components via entering and exiting props.

import Animated, { FadeIn, FadeOutLeft } from "react-native-reanimated";

const ANIMATION_DURATION = 400;
const ANIMATION_DELAY = 100;

function NotificationBanner({ visible }: { visible: boolean }) {
  if (!visible) return null;

  return (
    <Animated.View
      entering={FadeIn.duration(ANIMATION_DURATION).delay(ANIMATION_DELAY)}
      exiting={FadeOutLeft.duration(ANIMATION_DURATION)}
      style={styles.banner}
    >
      <Text>New notification</Text>
    </Animated.View>
  );
}

Available builders: FadeIn, SlideInRight, ZoomIn, BounceIn, FlipInEasyX, LightSpeedInRight, RotateIn, PinwheelIn, and all directional variants (Up/Down/Left/Right) plus corresponding Out variants.

Modifiers: .duration(ms), .delay(ms), .springify() (with .damping(), .stiffness(), .mass()), .withInitialValues(), .withCallback(), .reduceMotion().

Performance tip: Define animation builders outside components or in useMemo -- creating them inline in render causes unnecessary object allocation.

See examples/layout-animations.md for custom builders, staggered lists, and EntryExitTransition.


Pattern 4: Gesture Integration

Reanimated integrates with react-native-gesture-handler. Gesture callbacks are automatically workletized -- you can access shared values directly without the 'worklet' directive.

import { Gesture, GestureDetector } from "react-native-gesture-handler";
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withDecay,
} from "react-native-reanimated";

function DraggableCard() {
  const offsetX = useSharedValue(0);

  const pan = Gesture.Pan()
    .onChange((e) => {
      offsetX.value += e.changeX;
    })
    .onFinalize((e) => {
      offsetX.value = withDecay({ velocity: e.velocityX, rubberBandEffect: true });
    });

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ translateX: offsetX.value }],
  }));

  return (
    <GestureDetector gesture={pan}>
      <Animated.View style={[styles.card, animatedStyle]} />
    </GestureDetector>
  );
}

Why good: onChange gives delta values (not absolute), onFinalize adds momentum with withDecay, gesture callbacks access shared values directly on UI thread

Key points:

  • Use onChange for incremental updates (delta), onUpdate for absolute position
  • GestureHandlerRootView must wrap your app near the root
  • This skill covers the Reanimated side of gesture animations -- for gesture configuration details (tap, pinch, fling, simultaneous gestures), see the gesture handler skill

See examples/gestures.md for swipe-to-dismiss, bottom sheet, and combined gestures.


Pattern 5: Scroll-Driven Animations

Use useScrollOffset to track scroll position as a shared value. Combine with interpolate for parallax, collapsing headers, and fade effects.

import Animated, {
  useAnimatedRef,
  useScrollOffset,
  useAnimatedStyle,
  interpolate,
  Extrapolation,
} from "react-native-reanimated";

const HEADER_MAX = 200;
const HEADER_MIN = 60;

function CollapsibleHeader() {
  const scrollRef = useAnimatedRef<Animated.ScrollView>();
  const scrollOffset = useScrollOffset(scrollRef);

  const headerStyle = useAnimatedStyle(() => ({
    height: interpolate(
      scrollOffset.value,
      [0, HEADER_MAX - HEADER_MIN],
      [HEADER_MAX, HEADER_MIN],
      Extrapolation.CLAMP
    ),
  }));

  return (
    <>
      <Animated.View style={[styles.header, headerStyle]} />
      <Animated.ScrollView ref={scrollRef}>
        {/* content */}
      </Animated.ScrollView>
    </>
  );
}

Why good: useScrollOffset auto-detects horizontal/vertical, no manual scroll event handler needed, Extrapolation.CLAMP prevents values outside the range

Reanimated 4 rename: useScrollViewOffset -> useScrollOffset (deprecated alias remains temporarily).

See examples/scroll-animations.md for parallax, sticky elements, and scroll-to-hide tab bar.


Pattern 6: Interpolation

Map one value range to another. interpolate for numbers, interpolateColor for color transitions.

import { interpolate, interpolateColor, Extrapolation } from "react-native-reanimated";

// Number interpolation: scroll position -> opacity
const opacity = interpolate(
  scrollY.value,
  [0, 100],    // input range
  [1, 0],      // output range
  Extrapolation.CLAMP
);

// Color interpolation: progress -> background
const backgroundColor = interpolateColor(
  progress.value,
  [0, 0.5, 1],                      // input range
  ["#FF0000", "#FFFF00", "#00FF00"]  // output colors (red -> yellow -> green)
);

Extrapolation options: CLAMP (cap at edges), EXTEND (extrapolate linearly), IDENTITY (return input value). Can set left/right independently: { extrapolateLeft: Extrapolation.CLAMP, extrapolateRight: Extrapolation.EXTEND }.

interpolateColor modes: 'RGB' (default) or 'HSV'. HSV produces more perceptually uniform transitions for hue changes.

See examples/core.md for multi-step interpolation and color transition examples.


Pattern 7: Worklet Functions

Functions that run on the UI thread. Mark with 'worklet' directive. Reanimated auto-workletizes callbacks in useAnimatedStyle, gesture handlers, and animation callbacks -- you only need explicit 'worklet' for standalone helper functions.

import { scheduleOnRN } from "react-native-worklets";

function clampValue(value: number, min: number, max: number) {
  "worklet";
  return Math.min(Math.max(value, min), max);
}

// Use in useAnimatedStyle -- auto-workletized, no directive needed
const style = useAnimatedStyle(() => ({
  opacity: clampValue(progress.value, 0, 1),
}));

Reanimated 4 threading changes:

Reanimated 3Reanimated 4 (react-native-worklets)
runOnJS(fn)("arg")scheduleOnRN(fn, "arg")
runOnUI(fn)("arg")scheduleOnUI(fn, "arg")

When you need explicit 'worklet':

  • Standalone helper functions called from other worklets
  • Functions passed to scheduleOnUI

When you do NOT need it:

  • useAnimatedStyle callbacks (auto-workletized)
  • Gesture handler callbacks (auto-workletized)
  • Animation callbacks (auto-workletized)

Pattern 8: CSS Animations and Transitions (Reanimated 4)

Declarative animation API modeled after web CSS. Best for state-driven animations. Worklet API remains for gesture/scroll-driven scenarios.

import Animated from "react-native-reanimated";

const TRANSITION_DURATION = 300;

function ToggleBox({ expanded }: { expanded: boolean }) {
  return (
    <Animated.View
      style={{
        height: expanded ? 200 : 60,
        opacity: expanded ? 1 : 0.5,
        transitionProperty: "height, opacity",
        transitionDuration: `${TRANSITION_DURATION}ms`,
        transitionTimingFunction: "ease-in-out",
      }}
    />
  );
}

CSS animation keyframes:

const PULSE_DURATION = 1000;

<Animated.View
  style={{
    animationName: {
      from: { transform: [{ scale: 1 }] },
      to: { transform: [{ scale: 1.1 }] },
    },
    animationDuration: `${PULSE_DURATION}ms`,
    animationIterationCount: "infinite",
    animationDirection: "alternate",
    animationTimingFunction: "ease-in-out",
  }}
/>

When to use CSS vs worklets:

  • CSS -- state-driven toggles, hover effects, simple transitions (less code, better optimizable)
  • Worklets -- gesture-driven, scroll-driven, frame-by-frame control, complex orchestration
</patterns>

<red_flags>

RED FLAGS

High Priority Issues:

  • Passing animated styles to regular View/Text instead of Animated.View/Animated.Text -- causes silent failure or crash
  • Mutating shared values inside useAnimatedStyle -- causes infinite re-evaluation loops
  • Using react-native-reanimated/plugin in Babel config with Reanimated 4 -- must use react-native-worklets/plugin (and it must be last in the plugins array)
  • Using Reanimated 4.x with Legacy Architecture (old bridge) -- Reanimated 4 is New Architecture only
  • Animating static properties in useAnimatedStyle instead of keeping them in StyleSheet -- wastes UI thread resources
  • Using restDisplacementThreshold/restSpeedThreshold in withSpring -- removed in v4, replaced by energyThreshold

Medium Priority Issues:

  • Creating layout animation builders inline in render -- allocates objects every render; define outside component or in useMemo
  • Using runOnJS/runOnUI instead of scheduleOnRN/scheduleOnUI -- old API moved to react-native-worklets
  • Missing GestureHandlerRootView at app root -- gestures silently fail without it
  • Using useAnimatedGestureHandler -- removed in v4, migrate to Gesture Handler 2's Gesture API

Gotchas and Edge Cases:

  • withSpring duration: actual completion time = perceptual duration x 1.5 -- divide v3 duration values by 1.5 when migrating
  • useScrollOffset renamed from useScrollViewOffset -- deprecated alias still works temporarily
  • Shared value .value access is synchronous on UI thread but asynchronous on JS thread -- don't rely on immediate reads after writes on JS thread
  • useWorkletCallback removed -- replace with useCallback + 'worklet' directive
  • React Compiler compatibility: use sv.get() and sv.set() instead of direct .value access when using React Compiler
  • combineTransition removed -- use EntryExitTransition.entering(entering).exiting(exiting)
  • Object shared values: reassign the entire object, never mutate individual properties -- mutations break reactivity tracking
  • Removing an animated style from a view does not unset the animated values -- explicitly set properties to undefined to reset
  • On New Architecture, layout animations use nativeID internally -- don't overwrite it on animated components

</red_flags>


<critical_reminders>

CRITICAL REMINDERS

All code must follow project conventions in CLAUDE.md

(You MUST use Animated components (Animated.View, Animated.Text, etc.) for any animated styles -- passing animated styles to regular components causes errors)

(You MUST keep static styles in StyleSheet.create and only animate dynamic properties in useAnimatedStyle -- animating static values wastes UI thread resources)

(You MUST NOT mutate shared values inside useAnimatedStyle callbacks -- read only, or you cause infinite loops)

(You MUST use react-native-worklets as a separate dependency in Reanimated 4 -- the worklet Babel plugin moved from react-native-reanimated/plugin to react-native-worklets/plugin)

Failure to follow these rules will cause animation failures, infinite loops, and crashes on the UI thread.

</critical_reminders>

What ships with it: 5 files

36.0 KB alongside SKILL.md

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.