Core Concepts

Layers

Understand the screen-container layering system: backdrop, content, deprecated surface wrappers, element targets, and navigation masks.

Mental Model

screenStyleInterpolator returns a keyed style map. Some keys target screen-container layers. Everything else targets transition-aware elements inside the screen tree.

The layer keys are:

  • backdrop
  • content
  • surface
  • NAVIGATION_MASK_CONTAINER_STYLE_ID
  • NAVIGATION_MASK_ELEMENT_STYLE_ID

Any other key is treated as an element target and matched against a styleId on a transition-aware component such as Transition.View or Transition.Pressable.

Layer Map

TSX

1import {
2 NAVIGATION_MASK_CONTAINER_STYLE_ID,
3 NAVIGATION_MASK_ELEMENT_STYLE_ID,
4} from "react-native-screen-transitions";
5
6return {
7 backdrop: { style: { ... }, props: { ... } },
8 content: { style: { ... }, props: { ... } },
9 surface: { style: { ... }, props: { ... } },
10 [NAVIGATION_MASK_CONTAINER_STYLE_ID]: { style: { ... } },
11 [NAVIGATION_MASK_ELEMENT_STYLE_ID]: { style: { ... } },
12 ["hero-card"]: { style: { ... }, props: { ... } },
13};

Each slot also supports shorthand style-only output:

TSX

1return {
2 backdrop: { opacity: 0.4 },
3 content: { transform: [{ translateY: 12 }] },
4 "hero-card": { scale: 0.98 },
5};

Render Order

Inside screen-container, layers are resolved in this order:

  1. backdrop
  2. content
  3. optional navigation mask wrapper when navigationMaskEnabled is enabled
  4. deprecated optional surface wrapper when surfaceComponent is provided
  5. screen children and any matching styleId targets

That gives you two levels of control:

  • screen-level layers for the overall container structure
  • element-level targets for individual transition-aware components inside the screen

backdrop

backdrop controls the full-screen layer behind the active screen content.

TSX

1backdrop: {
2 style: {
3 opacity: interpolate(progress, [0, 1, 2], [0, 0.4, 0]),
4 },
5}

If you provide backdropComponent, pass either a component type or a render-style function component.

TSX

1options={{
2 backdropBehavior: "dismiss",
3 backdropComponent: BlurView,
4}}

Function components receive the layer values directly:

TSX

1options={{
2 backdropBehavior: "dismiss",
3 backdropComponent: ({ styles, props, pointerEvents }) => (
4 <Animated.View
5 style={styles}
6 animatedProps={props}
7 pointerEvents={pointerEvents}
8 />
9 ),
10}}

The render props are:

  • styles: the base backdrop style plus animated styles from the backdrop slot
  • props: animated props from the backdrop slot
  • pointerEvents: the backdrop pointer-event state

Use backdrop for dimming, blur intensity, tint, or any visual treatment outside the screen itself. backdropBehavior still controls how taps are handled.

content

content targets the root animated container for the screen itself.

TSX

1content: {
2 style: {
3 transform: [{ translateY: interpolate(progress, [0, 1], [40, 0]) }],
4 },
5}

This is the slot that usually carries the main screen motion: translate, scale, opacity, rotation, and overall clipping styles.

Use contentComponent when the content layer itself needs a custom shell component.

TSX

1options={{
2 contentComponent: ({ styles, props, pointerEvents, children }) => (
3 <Animated.View
4 style={styles}
5 animatedProps={props}
6 pointerEvents={pointerEvents}
7 >
8 {children}
9 </Animated.View>
10 ),
11}}

The render props are:

  • styles: the base content style plus animated styles from the content slot
  • props: animated props from the content slot
  • pointerEvents: the content pointer-event state
  • children: the screen subtree

Use contentComponent for custom tray shells, rounded containers, glass panels, or other screen-level wrappers that should own the screen subtree.

surface

surface targets the optional wrapper created by surfaceComponent.

surfaceComponent is deprecated. It still works for compatibility, but new custom shells should use contentComponent instead.

TSX

1options={{
2 surfaceComponent: SquircleView,
3}}
4
5surface: {
6 style: {
7 borderRadius: interpolate(progress, [0, 1], [28, 0]),
8 },
9 props: {
10 cornerRadius: interpolate(progress, [0, 1], [28, 0]),
11 },
12}

Use surface only for older code that still relies on surfaceComponent. For new code, put the custom shell in contentComponent and drive it with the content slot.

Element Targets

Any non-layer key is routed to transition-aware elements inside the screen tree.

TSX

1<Transition.View styleId="hero-card">
2 <Card />
3</Transition.View>
4
5return {
6 "hero-card": {
7 style: {
8 opacity: interpolate(progress, [0, 1], [0, 1]),
9 transform: [{ scale: interpolate(progress, [0, 1], [0.94, 1]) }],
10 },
11 },
12};

These keys apply only to transition-aware components created by the library such as Transition.View, Transition.Pressable, Transition.ScrollView, and Transition.FlatList.

When navigationMaskEnabled is enabled and @react-native-masked-view/masked-view is installed, the content layer is wrapped in a masked container.

Two exported reserved ids control that wrapper:

  • NAVIGATION_MASK_CONTAINER_STYLE_ID
  • NAVIGATION_MASK_ELEMENT_STYLE_ID

Use them directly in the interpolator:

TSX

1import {
2 NAVIGATION_MASK_CONTAINER_STYLE_ID,
3 NAVIGATION_MASK_ELEMENT_STYLE_ID,
4} from "react-native-screen-transitions";
5
6<Stack.Screen
7 name="Sheet"
8 component={SheetScreen}
9 options={{
10 navigationMaskEnabled: true,
11 screenStyleInterpolator: ({ current, progress }) => {
12 "worklet";
13
14 const { height } = current.layouts.screen;
15 const sheetHeight = interpolate(progress, [0, 1], [height * 0.6, height]);
16
17 return {
18 [NAVIGATION_MASK_CONTAINER_STYLE_ID]: {
19 style: {
20 transform: [{ translateY: height - sheetHeight }],
21 },
22 },
23 [NAVIGATION_MASK_ELEMENT_STYLE_ID]: {
24 style: {
25 width: "100%",
26 height: sheetHeight,
27 borderRadius: interpolate(progress, [0, 1], [32, 0]),
28 backgroundColor: "white",
29 },
30 },
31 };
32 },
33 }}
34/>

NAVIGATION_MASK_CONTAINER_STYLE_ID styles the masked content container. NAVIGATION_MASK_ELEMENT_STYLE_ID styles the mask element itself.

Treat both ids as reserved layer keys. Do not reuse them as custom styleId values.

Props vs Style

Every slot supports both animated styles and animated props:

TSX

1return {
2 backdrop: {
3 style: { opacity: 1 },
4 props: { intensity: 80 },
5 },
6 content: {
7 style: { borderRadius: 24 },
8 },
9 surface: {
10 props: { cornerRadius: 24 },
11 },
12};

Use style for visual layout and transforms. Use props only when the wrapped layer or target component exposes animatable props such as blur intensity.