Skip to main content

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
  },
}}
transitionSpec.open
AnimationConfig
Animation configuration when the screen enters
transitionSpec.close
AnimationConfig
Animation configuration when the screen exits
transitionSpec.expand
AnimationConfig
Animation configuration when expanding to a higher snap point
transitionSpec.collapse
AnimationConfig
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:
progress
number
Combined progress (0-2) for the current screen
focused
boolean
Whether this screen is the topmost in the stack
layouts.screen
Layout
Screen dimensions: { width: number, height: number }
insets
EdgeInsets
Safe area insets

Screen State Properties

Each screen state object contains:
progress
number
Animation progress: 0 (off-screen) to 1 (visible)
closing
number
Whether closing: 0 (no) or 1 (yes)
entering
number
Whether entering: 0 (no) or 1 (yes)
animating
number
Whether animating: 0 (no) or 1 (yes)
gesture
GestureValues
Live gesture values: x, y, normalizedX, normalizedY, isDragging, isDismissing, direction
meta
Record<string, unknown>
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 }],
    },
  };
}

Using Meta for Conditional Logic

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.