| name | react-native-ease-refactor |
| description | Scan for Animated/Reanimated code and migrate to EaseView |
| user-invocable | true |
react-native-ease refactor
You are a migration assistant that converts react-native-reanimated and React Native's built-in Animated API code to react-native-ease EaseView components.
Follow these 6 phases exactly. Do not skip phases or reorder them.
Phase 1: Discovery
Scan the user's project for animation code:
-
Use Grep to detect if the project uses NativeWind:
- Pattern:
from ['"]nativewind['"] in **/*.{ts,tsx,js,jsx}
- Also check
package.json for "nativewind" in dependencies
- If NativeWind is detected, set a flag
usesNativeWind = true for use in Phase 5
-
Detect the Reanimated version (needed for default value mapping in Phase 2):
- Read
package.json and check the react-native-reanimated version in dependencies or devDependencies
- If the version is
^4 or >=4.0.0, set reanimatedVersion = 4
- Otherwise set
reanimatedVersion = 3 (covers v2/v3 which share the same defaults)
-
Use Grep to find all files importing from react-native-reanimated:
- Pattern:
from ['"]react-native-reanimated['"]
- Search in
**/*.{ts,tsx,js,jsx}
-
Use Grep to find all files using React Native's built-in Animated API:
- Pattern:
from ['"]react-native['"] that also use Animated
- Pattern:
Animated\.View|Animated\.Text|Animated\.Image|Animated\.Value|Animated\.timing|Animated\.spring
-
Use Grep to find files already using react-native-ease (to avoid re-migrating):
- Pattern:
from ['"]react-native-ease['"]
-
Read each file that contains animation code. Build a list of components with their animation patterns.
Exclude from scanning:
node_modules/
*.test.* and *.spec.* files
- Build output directories (
lib/, build/, dist/)
Phase 2: Classification
For each component found, classify as migratable or not migratable.
Decision Tree
Apply these checks in order. The first match determines the result:
- Uses gesture APIs? (
Gesture.Pan, Gesture.Pinch, Gesture.Rotation, useAnimatedGestureHandler) → NOT migratable — "Gesture-driven animation"
- Uses scroll handler? (
useAnimatedScrollHandler, onScroll with Animated.event) → NOT migratable — "Scroll-driven animation"
- Uses shared element transitions? (
sharedTransitionTag) → NOT migratable — "Shared element transition"
- Uses
runOnUI or worklet directives? → NOT migratable — "Requires worklet runtime"
- Uses
withSequence? → NOT migratable — "Animation sequencing not supported"
5b. Uses withDelay wrapping a single animation (withTiming/withSpring)? → MIGRATABLE — map to delay on the transition
5c. Uses withDelay wrapping withSequence or nested withDelay? → NOT migratable — "Complex delay/sequencing not supported"
- Uses complex
interpolate()? (more than 2 input/output values) → NOT migratable — "Complex interpolation"
- Uses
layout={...} prop? → NOT migratable — "Layout animation"
- Animates unsupported properties? (anything besides: opacity, translateX, translateY, scale, scaleX, scaleY, rotate, rotateX, rotateY, borderRadius, backgroundColor, borderWidth, borderColor, shadowOpacity, shadowRadius, shadowColor, shadowOffset, elevation) → NOT migratable — "Animates unsupported property:
<prop>"
- Uses different transition configs per property? (e.g., opacity uses 200ms timing, scale uses spring) → MIGRATABLE — map to
TransitionMap with category keys (transform, opacity, borderRadius, backgroundColor, border, shadow, default)
- Not driven by state? (animation triggered by gesture/scroll value, not React state) → NOT migratable — "Not state-driven"
- Otherwise → MIGRATABLE
Migratable Pattern Mapping
Use this table to convert Reanimated/Animated patterns to EaseView:
| Reanimated / Animated Pattern | EaseView Equivalent |
|---|
useSharedValue + useAnimatedStyle + withTiming for opacity, translate, scale, rotate, borderRadius, backgroundColor | animate={{ prop: value }} + transition={{ type: 'timing', duration, easing }} |
withSpring | transition={{ type: 'spring', damping, stiffness, mass, velocity }} |
entering={FadeIn} / FadeIn.duration(N) | initialAnimate={{ opacity: 0 }} + animate={{ opacity: 1 }} + timing transition |
entering={FadeInDown} / FadeInUp | initialAnimate={{ opacity: 0, translateY: ±value }} + animate={{ opacity: 1, translateY: 0 }} |
entering={SlideInLeft} / SlideInRight | initialAnimate={{ translateX: ±value }} + animate={{ translateX: 0 }} |
entering={SlideInUp} / SlideInDown | initialAnimate={{ translateY: ±value }} + animate={{ translateY: 0 }} |
entering={ZoomIn} | initialAnimate={{ scale: 0 }} + animate={{ scale: 1 }} |
exiting={FadeOut} / other exit animations | State-driven exit: boolean state + onTransitionEnd to unmount (flag as "requires state changes" in report) |
withRepeat(withTiming(...), -1, false) | transition={{ type: 'timing', ..., loop: 'repeat' }} + initialAnimate for start value |
withRepeat(withTiming(...), -1, true) | transition={{ type: 'timing', ..., loop: 'reverse' }} + initialAnimate for start value |
Easing.linear | easing: 'linear' |
Easing.ease / Easing.inOut(Easing.ease) | easing: 'easeInOut' |
Easing.in(Easing.ease) | easing: 'easeIn' |
Easing.out(Easing.ease) | easing: 'easeOut' |
Easing.bezier(x1, y1, x2, y2) | easing: [x1, y1, x2, y2] |
Animated.Value + Animated.timing | Same animate + transition pattern — convert to state-driven |
Animated.Value + Animated.spring | animate + transition={{ type: 'spring' }} — convert to state-driven |
withDelay(ms, withTiming(...)) or withDelay(ms, withSpring(...)) | transition={{ ..., delay: ms }} — add delay to the transition config |
entering={FadeIn.delay(ms)} / any entering preset with .delay() | initialAnimate + animate + transition={{ ..., delay: ms }} |
Different withTiming/withSpring per property in useAnimatedStyle | transition={{ opacity: { type: 'timing', ... }, transform: { type: 'spring', ... } }} (per-property map) |
Default Value Mapping
CRITICAL: Reanimated and EaseView have different defaults. You MUST explicitly set values to preserve the original animation behavior. Do not rely on EaseView defaults matching Reanimated defaults.
Use reanimatedVersion from Phase 1 to select the correct defaults.
withSpring → EaseView spring
Reanimated v2/v3 defaults:
| Parameter | Reanimated v2/v3 | EaseView default | Action |
|---|
damping | 10 | 15 | Must set damping: 10 |
stiffness | 100 | 120 | Must set stiffness: 100 |
mass | 1 | 1 | Same — omit |
velocity | 0 | 0 | Same — carry over only when set |
Reanimated v4 defaults:
| Parameter | Reanimated v4 | EaseView default | Action |
|---|
damping | 120 | 15 | Must set damping: 120 |
stiffness | 900 | 120 | Must set stiffness: 900 |
mass | 4 | 1 | Must set mass: 4 |
velocity | 0 | 0 | Same — carry over only when set |
Reanimated v4 changed to a critically damped, snappy spring (no bounce) as the default. The rationale was that the old physics-based defaults were too sensitive to start/end conditions. v4 recommends using duration + dampingRatio instead of raw physics params.
If the source code explicitly sets any of these values, carry them over as-is. If the source relies on Reanimated defaults (no explicit value), set the Reanimated default explicitly on the EaseView transition.
Example — bare withSpring(1) with no config:
scale.value = withSpring(1);
transition={{ type: 'spring', damping: 10, stiffness: 100 }}
transition={{ type: 'spring', damping: 120, stiffness: 900, mass: 4 }}
Duration-based spring: Reanimated v3+ also supports withSpring(target, { duration, dampingRatio }). If the code explicitly sets dampingRatio/duration, convert using: damping = dampingRatio * 2 * sqrt(stiffness * mass).
withTiming → EaseView timing
| Parameter | Reanimated default | EaseView default | Action |
|---|
duration | 300 | 300 | Same — omit |
easing | Easing.inOut(Easing.quad) | 'easeInOut' (cubic) | Must set easing: [0.455, 0.03, 0.515, 0.955] |
The easing curves are different! Reanimated's default is quadratic ease-in-out, EaseView's is cubic. Always set the easing explicitly when the source doesn't specify one.
Example — bare withTiming(1) with no config:
opacity.value = withTiming(1);
transition={{ type: 'timing', duration: 300, easing: [0.455, 0.03, 0.515, 0.955] }}
If the source explicitly sets an easing, map it using the easing table above.
Animated.timing (old RN API) → EaseView timing
| Parameter | RN Animated default | EaseView default | Action |
|---|
duration | 500 | 300 | Must set duration: 500 |
easing | Easing.inOut(Easing.ease) | 'easeInOut' | Same curve — omit |
Animated.spring (old RN API) → EaseView spring
RN Animated uses friction/tension by default: friction: 7, tension: 40. These map to: stiffness = tension, damping = friction.
| Parameter | RN Animated default | EaseView default | Action |
|---|