π Lightweight declarative animations powered by platform APIs
> /plugin marketplace add appandflow/react-native-ease> /plugin install react-native-ease@react-native-ease-plugins
Repo: appandflow/react-native-ease
What's inside
Lightweight declarative animations powered by platform APIs. Uses Core Animation on iOS and Animator on Android β zero JS overhead.
App & Flow is a Montreal-based React Native engineering and consulting studio. We partner with the worldβs top companies and are recommended by Expo. Need a hand? Letβs build together. team@appandflow.com
npm install react-native-ease
# or
yarn add react-native-ease
If you're already using react-native-reanimated or React Native's Animated API, this project includes an Agent Skill that scans your codebase for animations that can be replaced with react-native-ease and migrates them automatically.
npx skills add appandflow/react-native-ease
Then invoke the skill in your agent (e.g., /react-native-ease-refactor in Claude Code).
The skill will:
If you're using NativeWind (v4+), add this import once in your app's entry point (e.g., _layout.tsx or App.tsx):
import 'react-native-ease/nativewind';
This registers EaseView with NativeWind's cssInterop so className is properly converted to styles:
<EaseView
className="flex-1 bg-white rounded-2xl p-4"
animate={{ opacity: visible ? 1 : 0 }}
transition={{ type: 'timing', duration: 300 }}
>
{children}
</EaseView>
Tip: If you use the migration skill, it detects NativeWind automatically and adds this import for you.
If you're using Uniwind, first follow the Uniwind quickstart to install and configure Uniwind in your app. That setup includes the required CSS entry file, app-root CSS import, and bundler configuration.
Once Uniwind is set up, import EaseView from the Uniwind entry point:
import { EaseView } from 'react-native-ease/uniwind';
This wraps EaseView with Uniwind's withUniwind(...) so className is converted to styles:
<EaseView
className="flex-1 bg-white rounded-2xl p-4"
animate={{ opacity: visible ? 1 : 0 }}
transition={{ type: 'timing', duration: 300 }}
>
{children}
</EaseView>
import { EaseView } from 'react-native-ease';
function FadeCard({ visible, children }) {
return (
<EaseView
animate={{ opacity: visible ? 1 : 0 }}
transition={{ type: 'timing', duration: 300 }}
style={styles.card}
>
{children}
</EaseView>
);
}
EaseView works like a regular View β it accepts children, styles, and all standard view props. When values in animate change, it smoothly transitions to the new values using native platform animations.
| Use case | Ease | Reanimated |
|---|---|---|
| Fade/slide/scale on state change | β | |
| Enter/exit animations | β | |
| Gesture-driven animations (pan, pinch) | β | |
| Layout animations (width, height) | β | |
| Complex interpolations & chaining | β |
Timing animations transition from one value to another over a fixed duration with an easing curve.
<EaseView
animate={{ opacity: isVisible ? 1 : 0 }}
transition={{ type: 'timing', duration: 300, easing: 'easeOut' }}
/>
| Parameter | Type | Default | Description |
|---|---|---|---|
duration | number | 300 | Duration in milliseconds |
easing | EasingType | 'easeInOut' | Easing curve (preset name or [x1, y1, x2, y2] cubic bezier) |
delay | number | 0 | Delay in milliseconds before the animation starts |
loop | string | β | 'repeat' restarts from the beginning, 'reverse' alternates direction |
Available easing curves:
'linear' β constant speed'easeIn' β starts slow, accelerates'easeOut' β starts fast, decelerates'easeInOut' β slow start and end, fast middle[x1, y1, x2, y2] β custom cubic bezier (same as CSS cubic-bezier())Pass a [x1, y1, x2, y2] tuple for custom cubic bezier curves. The values correspond to the two control points of the bezier curve, matching the CSS cubic-bezier() function.
// Standard Material Design easing
<EaseView
animate={{ opacity: isVisible ? 1 : 0 }}
transition={{ type: 'timing', duration: 300, easing: [0.4, 0, 0.2, 1] }}
/>
// Overshoot (y-values can exceed 0β1)
<EaseView
animate={{ scale: active ? 1.2 : 1 }}
transition={{ type: 'timing', duration: 500, easing: [0.68, -0.55, 0.265, 1.55] }}
/>
x-values (x1, x2) must be between 0 and 1. y-values can exceed this range to create overshoot effects.
Spring animations use a physics-based model for natural-feeling motion. Great for interactive elements.
<EaseView
animate={{ translateX: isOpen ? 200 : 0 }}
transition={{ type: 'spring', damping: 15, stiffness: 120, mass: 1 }}
/>
| Parameter | Type | Default | Description |
|---|---|---|---|
damping | number | 15 | Friction β higher values reduce oscillation |
stiffness | number | 120 | Spring constant β higher values mean faster animation |
mass | number | 1 | Mass of the object β higher values mean slower, more momentum |
velocity | number | 0 | Initial velocity in property units per second (native only) |
delay | number | 0 | Delay in milliseconds before the animation starts |
velocity gives the spring a head start, the way Animated.spring's velocity does β useful
when the animation continues a gesture that was already moving. It is signed in value space, so a
positive value means the property is already increasing:
<EaseView
animate={{ translateY: dismissed ? 400 : 0 }}
transition={{ type: 'spring', damping: 18, stiffness: 200, velocity: 1200 }}
/>
Units follow the JS side β DIPs per second for translateX/translateY, degrees per second for
rotate, and plain units per second for scale and opacity. It has no effect on web, where a
spring compiles to a single normalized CSS easing curve that cannot express a per-property
starting velocity.
Spring presets for common feels:
// Snappy (no bounce)
{ type: 'spring', damping: 20, stiffness: 300, mass: 1 }
// Gentle bounce
{ type: 'spring', damping: 12, stiffness: 120, mass: 1 }
// Bouncy
{ type: 'spring', damping: 8, stiffness: 200, mass: 1 }
// Slow and heavy
{ type: 'spring', damping: 20, stiffness: 60, mass: 2 }
Use { type: 'none' } to apply values immediately without animation. Useful for skipping animations in reduced-motion modes or when you need an instant state change.
<EaseView
animate={{ opacity: isVisible ? 1 : 0 }}
transition={{ type: 'none' }}
/>
onTransitionEnd fires immediately with { finished: true }.
Pass a map instead of a single config to use different animation types per property category.
<EaseView
animate={{ opacity: visible ? 1 : 0, translateY: visible ? 0 : 30 }}
transition={{
opacity: { type: 'timing', duration: 150, easing: 'easeOut' },
transform: { type: 'spring', damping: 12, stiffness: 200 },
}}
/>
Available category keys:
| Key | Properties |
|---|---|
default | Fallback for categories not explicitly listed |
transform | translateX, translateY, scaleX, scaleY, rotate, rotateX, rotateY |
opacity | opacity |
borderRadius | borderRadius |
backgroundColor | backgroundColor |
border | borderWidth, borderColor |
shadow | shadowOpacity, shadowRadius, shadowColor, shadowOffset, elevation |
Use default as a fallback for categories not explicitly listed:
<EaseView
animate={{ opacity: 1, scale: 1.2, translateY: -20 }}
transition={{
default: { type: 'spring', damping: 15, stiffness: 120 },
opacity: { type: 'timing', duration: 200, easing: 'easeOut' },
}}
/>
When no default key is provided, the library default (timing 300ms easeInOut) applies to all categories.
Android note: Android animates
backgroundColorwithValueAnimator(timing only). If a per-property map specifiestype: 'spring'forbackgroundColor, it silently falls back to timing 300ms.
borderRadius can be animated just like other properties. It uses hardware-accelerated platform APIs β ViewOutlineProvider + clipToOutline on Android and layer.cornerRadius on iOS.
<EaseView
animate={{ borderRadius: expanded ? 0 : 16 }}
transition={{ type: 'timing', duration: 300 }}
style={styles.card}
>
<Image source={heroImage} style={styles.image} />
<Text>Content is clipped to rounded corners</Text>
</EaseView>
When borderRadius is in animate, any borderRadius in style is automatically stripped to avoid conflicts.
backgroundColor can be animated using any React Native color value. Colors are converted to native ARGB integers via processColor().
<EaseView
animate={{ backgroundColor: isActive ? '#3B82F6' : '#E5E7EB' }}
transition={{ type: 'timing', duration: 300 }}
style={styles.card}
>
<Text>Tap to change color</Text>
</EaseView>
FAQ
react-native-ease is a Claude Code plugin with 1 hand-picked skill for development work, indexed on Flowy. Install it with the command on its page. It includes react-native-ease-refactor. Its skills do not fire on their own yet. Request auto-invocation to have Flowy route them as you prompt. Free and open source.
Is this plugin yours?
Claim it with GitHubSubmit a pluginPromote it