Skip to main content

Overview

The useScreenAnimation hook provides access to the current animation state and interpolation properties within a screen component. Use this hook to create custom animations that respond to navigation transitions, gestures, and screen state changes.

Import

import { useScreenAnimation } from 'react-native-screen-transitions';

Usage

import { useScreenAnimation } from 'react-native-screen-transitions';
import Animated, { useAnimatedStyle } from 'react-native-reanimated';

function DetailScreen() {
  const animation = useScreenAnimation();

  const style = useAnimatedStyle(() => ({
    opacity: animation.value.current.progress,
    transform: [
      {
        scale: interpolate(
          animation.value.progress,
          [0, 1, 2],
          [0.8, 1, 0.95]
        ),
      },
    ],
  }));

  return (
    <Animated.View style={[{ flex: 1 }, style]}>
      {/* Your screen content */}
    </Animated.View>
  );
}

Return Value

Returns a SharedValue<ScreenInterpolationProps> from Reanimated containing all interpolation properties.

ScreenInterpolationProps

progress
number
Combined progress of current and next screen transitions, ranging from 0-2:
  • 0: Screen entering
  • 1: Screen visible
  • 2: Screen exiting
stackProgress
number
Accumulated progress from the current screen’s position onwards in the stack. Ranges from 0-N where N is the number of screens above the current screen.
snapIndex
number
Animated index of the current snap point. Returns -1 if no snap points are defined. Interpolates between indices during gestures (e.g., 1.5 = halfway between snap 1 and 2).
focused
boolean
Whether the current screen is the focused (topmost) screen in the stack.
current
ScreenTransitionState
Values for the current screen being interpolated.Properties:
  • progress (number): Animation progress (0 = entering, 1 = visible)
  • closing (number): Whether screen is being dismissed (0 or 1)
  • entering (number): Whether screen is entering (0 or 1)
  • animating (number): Whether currently animating (0 or 1)
  • gesture (GestureValues): Live gesture values (x, y, normalized values, drag/dismiss state)
  • meta (Record<string, unknown>): Custom metadata from screen options
  • route (BaseStackRoute): The route object for this screen
previous
ScreenTransitionState | undefined
Values for the screen that came before the current one in the navigation stack. undefined if no previous screen exists.
next
ScreenTransitionState | undefined
Values for the screen that comes after the current one in the navigation stack. undefined if no next screen exists.
active
ScreenTransitionState
The screen state that is currently driving the transition (either current or next, whichever is focused).
inactive
ScreenTransitionState | undefined
The screen state that is NOT driving the transition. When focused, this is the previous screen. When not focused, this is the current screen.
layouts
object
Layout measurements for the screen.Properties:
  • screen (Layout): Object containing width and height of the screen container
insets
EdgeInsets
Safe area insets from react-native-safe-area-context. Contains top, bottom, left, and right values.
bounds
BoundsAccessor
Function that provides access to bounds builders for creating shared element transitions. See Shared Elements for usage.

Examples

Basic Fade Animation

import { useScreenAnimation } from 'react-native-screen-transitions';
import Animated, { useAnimatedStyle, interpolate } from 'react-native-reanimated';

function MyScreen() {
  const animation = useScreenAnimation();

  const fadeStyle = useAnimatedStyle(() => ({
    opacity: interpolate(
      animation.value.progress,
      [0, 1, 2],
      [0, 1, 0]
    ),
  }));

  return <Animated.View style={fadeStyle}>...</Animated.View>;
}

Animate Based on Gesture State

import { useScreenAnimation } from 'react-native-screen-transitions';
import Animated, { useAnimatedStyle } from 'react-native-reanimated';

function MyScreen() {
  const animation = useScreenAnimation();

  const gestureStyle = useAnimatedStyle(() => {
    const isDragging = animation.value.current.gesture.isDragging;
    const normalizedY = animation.value.current.gesture.normalizedY;
    
    return {
      opacity: isDragging ? 0.8 : 1,
      transform: [
        { translateY: normalizedY * 50 },
      ],
    };
  });

  return <Animated.View style={gestureStyle}>...</Animated.View>;
}

Respond to Snap Points

import { useScreenAnimation } from 'react-native-screen-transitions';
import Animated, { useAnimatedStyle, interpolate } from 'react-native-reanimated';

function BottomSheet() {
  const animation = useScreenAnimation();

  const headerStyle = useAnimatedStyle(() => ({
    // Fade in header as sheet expands from snap 0 to snap 1
    opacity: interpolate(
      animation.value.snapIndex,
      [0, 1],
      [0, 1]
    ),
  }));

  return (
    <Animated.View style={{ flex: 1 }}>
      <Animated.View style={headerStyle}>
        <Text>Sheet Header</Text>
      </Animated.View>
    </Animated.View>
  );
}

Stack Progress Animation

import { useScreenAnimation } from 'react-native-screen-transitions';
import Animated, { useAnimatedStyle, interpolate } from 'react-native-reanimated';

function HomeScreen() {
  const animation = useScreenAnimation();

  const style = useAnimatedStyle(() => ({
    // Scale down as more screens are pushed on top
    transform: [
      {
        scale: interpolate(
          animation.value.stackProgress,
          [0, 1, 2],
          [1, 0.95, 0.9]
        ),
      },
    ],
  }));

  return <Animated.View style={style}>...</Animated.View>;
}

Notes

All animations must be performed within useAnimatedStyle or other Reanimated hooks since the return value is a SharedValue that runs on the UI thread.
The progress value ranges from 0-2 where:
  • 0 → 1: Screen entering
  • 1: Screen fully visible
  • 1 → 2: Screen exiting