Overview
Custom animations in react-native-screen-transitions are built using the screenStyleInterpolator function. This function receives animation props and returns animated styles that control how your screen transitions.
The Progress Model
Every screen has a progress value that animates through three key states:
0 ─────────── 1 ─────────── 2
entering visible exiting
How It Works
When navigating from Screen A to Screen B:
- Screen B:
progress animates from 0 → 1 (entering)
- Screen A:
progress animates from 1 → 2 (exiting)
This unified model makes it easy to define both enter and exit animations in a single interpolator.
Basic Examples
Simple Fade
Create a fade transition by interpolating opacity:
options={{
screenStyleInterpolator: ({ progress }) => {
"worklet";
return {
contentStyle: {
opacity: interpolate(progress, [0, 1, 2], [0, 1, 0]),
},
};
},
}}
Slide from Right
Slide the screen in from the right edge:
options={{
screenStyleInterpolator: ({ progress, layouts: { screen } }) => {
"worklet";
return {
contentStyle: {
transform: [{
translateX: interpolate(
progress,
[0, 1, 2],
[screen.width, 0, -screen.width * 0.3]
),
}],
},
};
},
}}
Slide from Bottom
Create a modal-style transition:
options={{
screenStyleInterpolator: ({ progress, layouts: { screen } }) => {
"worklet";
return {
contentStyle: {
transform: [{
translateY: interpolate(progress, [0, 1], [screen.height, 0]),
}],
},
};
},
}}
Zoom In
Scale and fade the screen simultaneously:
import { Extrapolation, interpolate } from "react-native-reanimated";
options={{
screenStyleInterpolator: ({ progress }) => {
"worklet";
const scale = interpolate(
progress,
[0, 1, 2],
[0.5, 1, 0.5],
Extrapolation.CLAMP,
);
const opacity = interpolate(
progress,
[0, 1, 2],
[0, 1, 0],
Extrapolation.CLAMP,
);
return {
contentStyle: {
transform: [{ scale }],
opacity,
},
};
},
}}
Return Values
Your interpolator can return multiple style objects:
return {
contentStyle: { ... }, // Main screen content
backdropStyle: { ... }, // Semi-transparent backdrop layer
["my-id"]: { ... }, // Specific element via styleId
};
contentStyle
Applies styles to the main screen container. Use this for most transitions:
contentStyle: {
opacity: interpolate(progress, [0, 1], [0, 1]),
transform: [{ scale: interpolate(progress, [0, 1], [0.9, 1]) }],
}
backdropStyle
Controls the backdrop layer behind the screen. Useful for modal-style transitions:
import { interpolateColor } from "react-native-reanimated";
backdropStyle: {
backgroundColor: interpolateColor(
progress,
[0, 1],
["rgba(0,0,0,0)", "rgba(0,0,0,0.5)"]
),
}
Custom Style IDs
Target specific elements using styleId:
// In your interpolator
return {
"hero-image": {
opacity: interpolate(progress, [0, 1], [0, 1]),
transform: [{ scale: interpolate(progress, [0, 1], [0.8, 1]) }],
},
};
// In your component
<Transition.View styleId="hero-image">
<Image source={...} />
</Transition.View>
Animation Specs
Control the timing and feel of your animations using spring configurations:
options={{
screenStyleInterpolator: myInterpolator,
transitionSpec: {
open: { stiffness: 1000, damping: 500, mass: 3 }, // Screen enters
close: { stiffness: 1000, damping: 500, mass: 3 }, // Screen exits
expand: { stiffness: 300, damping: 30 }, // Snap point increases
collapse: { stiffness: 300, damping: 30 }, // Snap point decreases
},
}}
Animation configuration when the screen enters
Animation configuration when the screen exits
Animation configuration when expanding to a higher snap point
Animation configuration when collapsing to a lower snap point
Using Default Specs
The library exports a default spring configuration you can use:
import Transition from "react-native-screen-transitions";
options={{
screenStyleInterpolator: myInterpolator,
transitionSpec: {
open: Transition.Specs.DefaultSpec,
close: Transition.Specs.DefaultSpec,
},
}}
The DefaultSpec provides balanced spring physics:
{
stiffness: 1000,
damping: 500,
mass: 3,
overshootClamping: true,
restSpeedThreshold: 0.02,
}
Advanced Props
The screenStyleInterpolator receives comprehensive animation state:
Basic Props
Screen States
Advanced
Combined progress (0-2) for the current screen
Whether this screen is the topmost in the stack
Screen dimensions: { width: number, height: number }
Values for the current screen being interpolated
previous
ScreenTransitionState | undefined
Values for the screen before the current one
next
ScreenTransitionState | undefined
Values for the screen after the current one
The screen currently driving the transition
inactive
ScreenTransitionState | undefined
The screen NOT driving the transition
Accumulated progress across the entire stack (0-N)
Animated snap point index (-1 if no snap points)
Function for creating shared element transitions
Screen State Properties
Each screen state object contains:
Animation progress: 0 (off-screen) to 1 (visible)
Whether closing: 0 (no) or 1 (yes)
Whether entering: 0 (no) or 1 (yes)
Whether animating: 0 (no) or 1 (yes)
Live gesture values: x, y, normalizedX, normalizedY, isDragging, isDismissing, direction
Custom metadata from screen options
Using Gesture Values
Access live gesture data for interactive animations:
screenStyleInterpolator: ({ current, layouts: { screen } }) => {
"worklet";
const translateX = interpolate(
current.gesture.normalizedX,
[-1, 0, 1],
[-screen.width * 0.5, 0, screen.width * 0.5],
"clamp",
);
return {
contentStyle: {
transform: [{ translateX }],
},
};
}
Pass custom data between screens for conditional animations:
// Screen A configuration
options={{
meta: { hideTabBar: true, scalesOthers: true }
}}
// Screen B reads it
screenStyleInterpolator: (props) => {
"worklet";
const shouldScale = props.inactive?.meta?.scalesOthers;
const scale = shouldScale
? interpolate(props.progress, [0, 1], [1, 0.9])
: 1;
return {
contentStyle: {
transform: [{ scale }],
},
};
}
Worklet Directive
Always include the "worklet" directive at the start of your interpolator function. This tells Reanimated to run your animation code on the UI thread for smooth 60fps+ animations.
screenStyleInterpolator: ({ progress }) => {
"worklet"; // Required!
// ... your animation code
}
Real-World Examples
Check out the built-in presets for inspiration:
- DraggableCard: Multi-directional drag with scaling
- ElasticCard: Elastic drag effect with backdrop
- SlideFromTop: Vertical slide from top edge
- SlideFromBottom: Modal-style slide up
- ZoomIn: Scale and fade transition
See Presets Overview for complete implementations.