Skip to main content

Your First Transition

The simplest way to get started is using a preset animation with a Stack navigator:
import { Transition } from 'react-native-screen-transitions';
import { View, Text, Pressable } from 'react-native';
import { useNavigation } from '@react-navigation/native';

export default function HomeScreen() {
  const navigation = useNavigation();

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text style={{ fontSize: 24, marginBottom: 20 }}>Home</Text>
      <Pressable
        onPress={() => navigation.navigate('Details')}
        style={{ padding: 10, backgroundColor: '#007AFF', borderRadius: 8 }}
      >
        <Text style={{ color: 'white' }}>Go to Details</Text>
      </Pressable>
    </View>
  );
}

export function RootStack() {
  return (
    <Transition.Stack
      screenOptions={{
        headerShown: false,
        animationEnabled: true,
        transitionSpec: 'default',
      }}
    >
      <Transition.Screen name="Home" component={HomeScreen} />
      <Transition.Screen name="Details" component={DetailsScreen} />
    </Transition.Stack>
  );
}

Using Preset Animations

The library includes several built-in animations. Specify them with transitionSpec:
<Transition.Stack
  screenOptions={{
    transitionSpec: 'slide', // 'default', 'slide', 'fade', 'zoom'
  }}
>
  {/* screens */}
</Transition.Stack>

Custom Animations with Slots

For more control, define a custom screenStyleInterpolator using the slot-based return format:
import { interpolate } from 'react-native-reanimated';

const customInterpolator = ({ progress }) => {
  "worklet";
  return {
    content: {
      style: {
        opacity: interpolate(progress, [0, 1], [0, 1]),
        transform: [
          {
            translateY: interpolate(progress, [0, 1], [100, 0]),
          },
        ],
      },
    },
    backdrop: {
      style: {
        opacity: interpolate(progress, [0, 1], [0, 0.5]),
      },
    },
  };
};

<Transition.Stack screenStyleInterpolator={customInterpolator}>
  {/* screens */}
</Transition.Stack>

Adding Gestures

Enable interactive dismissal with gesture control:
<Transition.Stack
  screenStyleInterpolator={customInterpolator}
  gestureEnabled
  gestureDirection="vertical"
  gestureActivationArea={{ top: 0, bottom: 0, left: 0, right: 0 }}
>
  {/* screens */}
</Transition.Stack>
Users can now drag the screen vertically to dismiss it. For modal-style overlays, use Transition.Modal:
<Transition.Modal
  name="Modal"
  screenStyleInterpolator={customInterpolator}
  gestureEnabled
  gestureDirection="vertical"
>
  {/* modal content */}
</Transition.Modal>

Using Snap Points (Sheets)

For bottom sheets with snap points, define positions where the sheet can rest:
const sheetInterpolator = ({ progress, layouts }) => {
  "worklet";
  const height = layouts.content?.height ?? 300;

  return {
    content: {
      style: {
        transform: [
          {
            translateY: interpolate(progress, [0, 1, 2], [height, 0, -100]),
          },
        ],
      },
    },
  };
};

<Transition.Modal
  screenStyleInterpolator={sheetInterpolator}
  snapPoints={[0.4, 1]}
  gestureEnabled
  sheetScrollGestureBehavior="expand-and-collapse"
>
  {/* sheet content */}
</Transition.Modal>

Auto Snap Points

For responsive sheets that size to content, use "auto":
<Transition.Modal
  screenStyleInterpolator={sheetInterpolator}
  snapPoints={["auto", 1]}
  gestureEnabled
>
  {/* content automatically sizes based on actual height */}
</Transition.Modal>
In your interpolator, access the measured height with layouts.content.height:
const sheetInterpolator = ({ progress, layouts }) => {
  "worklet";
  const contentHeight = layouts.content?.height ?? 300;

  return {
    content: {
      style: {
        transform: [
          {
            translateY: interpolate(
              progress,
              [0, 1, 2],
              [contentHeight, 0, -100]
            ),
          },
        ],
      },
    },
  };
};

Shared Element Animation

Link elements between screens with Transition.Boundary components:
// Home screen
<Transition.Boundary.Pressable
  id="avatar"
  onPress={() => navigation.navigate('Details')}
>
  <Image source={{ uri: 'https://...' }} style={{ width: 80, height: 80 }} />
</Transition.Boundary.Pressable>

// Details screen
<Transition.Boundary.View id="avatar">
  <Image source={{ uri: 'https://...' }} style={{ width: 200, height: 200 }} />
</Transition.Boundary.View>
Then animate the bounds in your interpolator:
const screenStyleInterpolator = ({ bounds }) => {
  "worklet";
  const avatarBounds = bounds({ id: 'avatar' });

  return {
    content: {
      style: {
        opacity: interpolate(progress, [0, 1], [0, 1]),
      },
    },
  };
};

Next Steps

Now that you understand the basics:
Start simple with presets, then customize when you need more control. The slot-based return format scales from basic to advanced animations.