Skip to main content

Overview

Version 3.4 is fully backward compatible with 3.3. All deprecated APIs continue to work, but this guide helps you migrate to the new patterns for better code clarity and future compatibility. Key changes:
  • Boundary components with id prop replace sharedBoundTag
  • Slot-based return format for animations
  • Auto snap points with snapPoints: ["auto"]
  • New gesture and animation options
  • Built-in navigationMaskEnabled replaces Transition.MaskedView

Step 1: Update Boundary Components

From sharedBoundTag

The sharedBoundTag prop on Transition.View and Transition.Pressable is deprecated. Before (v3.3):
<Transition.View sharedBoundTag="avatar">
  <Image source={avatar} style={{ width: 80, height: 80 }} />
</Transition.View>

<Transition.View sharedBoundTag="avatar">
  <Image source={avatar} style={{ width: 200, height: 200 }} />
</Transition.View>
After (v3.4):
// Source
<Transition.Boundary.Pressable id="avatar" onPress={navigate}>
  <Image source={avatar} style={{ width: 80, height: 80 }} />
</Transition.Boundary.Pressable>

// Destination
<Transition.Boundary.View id="avatar">
  <Image source={avatar} style={{ width: 200, height: 200 }} />
</Transition.Boundary.View>
Benefits:
  • Clearer intent: Boundary.View explicitly marks shared elements
  • Better composability: Separate boundary from content
  • Type-safe: Dedicated components with props validation

Step 2: Migrate Interpolator Return Format

The flat contentStyle/backdropStyle format is deprecated. Use the new slot-based format.

Before (v3.3)

screenStyleInterpolator: ({ progress }) => ({
  contentStyle: {
    opacity: interpolate(progress, [0, 1], [0, 1]),
    transform: [
      {
        translateY: interpolate(progress, [0, 1], [100, 0]),
      },
    ],
  },
  backdropStyle: {
    opacity: interpolate(progress, [0, 1], [0, 0.7]),
  },
})

After (v3.4)

screenStyleInterpolator: ({ progress }) => ({
  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.7]),
    },
  },
})
Migration steps:
  1. Rename contentStylecontent: { style: ... }
  2. Rename backdropStylebackdrop: { style: ... }
  3. Nest styles under style key
Full example:
// Before
const myInterpolator = ({ progress }) => ({
  contentStyle: {
    opacity: interpolate(progress, [0, 1], [0, 1]),
  },
  backdropStyle: {
    opacity: interpolate(progress, [0, 1], [0, 0.5]),
  },
});

// After
const myInterpolator = ({ progress }) => ({
  content: {
    style: {
      opacity: interpolate(progress, [0, 1], [0, 1]),
    },
  },
  backdrop: {
    style: {
      opacity: interpolate(progress, [0, 1], [0, 0.5]),
    },
  },
});

Step 3: Update Navigation Masking

Replace Transition.MaskedView with the navigationMaskEnabled prop.

Before (v3.3)

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

After (v3.4)

<Transition.Stack
  navigationMaskEnabled
  screenStyleInterpolator={interpolator}
>
  {/* screens */}
</Transition.Stack>
Benefits:
  • Simpler API: No wrapper component needed
  • Built-in optimization: Native-level masking
  • Cleaner code: One prop instead of wrapper

Step 4: Auto Snap Points

If you have dynamic content sizing, use snapPoints: ["auto"].

Before (v3.3)

<Transition.Modal
  snapPoints={[300, 600]}  // Fixed pixel values
  screenStyleInterpolator={({ progress }) => ({
    contentStyle: {
      transform: [{
        translateY: interpolate(progress, [0, 1], [600, 0]),
      }],
    },
  })}
>
  {/* Variable height content */}
</Transition.Modal>

After (v3.4)

<Transition.Modal
  snapPoints={["auto", 1]}  // Auto-size first snap point
  screenStyleInterpolator={({ progress, layouts }) => ({
    content: {
      style: {
        transform: [{
          translateY: interpolate(progress, [0, 1], [
            layouts.content?.height ?? 300,
            0,
          ]),
        }],
      },
    },
  })}
>
  {/* Content automatically sizes */}
</Transition.Modal>
Key points:
  • "auto" measures content intrinsic height
  • Access measured height via layouts.content.height
  • Perfect for dynamic content like forms, lists

Step 5: Update Scroll Gesture Behavior

Replace the expandViaScrollView boolean with explicit behavior.

Before (v3.3)

<Transition.Modal
  snapPoints={[0.5, 1]}
  expandViaScrollView={true}  // Boolean
>
  <ScrollView>
    {/* content */}
  </ScrollView>
</Transition.Modal>

After (v3.4)

<Transition.Modal
  snapPoints={[0.5, 1]}
  sheetScrollGestureBehavior="expand-and-collapse"  // Explicit values
>
  <ScrollView>
    {/* content */}
  </ScrollView>
</Transition.Modal>
Options:
  • "expand-and-collapse": Both expand and collapse work at scroll boundary (replaces true)
  • "collapse-only": Only collapse works; expand requires dead space (replaces false)

Step 6: New Gesture Options

v3.4 adds precise velocity control. Consider using these if you experienced velocity issues.
<Transition.Stack
  gestureEnabled
  gestureReleaseVelocityScale={1.5}  // NEW: Multiply velocity
  gestureReleaseVelocityMax={2000}   // NEW: Cap velocity
  gestureVelocityImpact={0.3}        // Existing
  snapVelocityImpact={0.1}           // Existing
>
  {/* screens */}
</Transition.Stack>

Step 7: Ancestor Targeting in Hooks

If using custom gesture hooks, update to new ancestor targeting syntax.

Before (v3.3)

useScreenGesture({
  gestureEnabled: true,
})

After (v3.4)

// Own gesture at this level
useScreenGesture("self", {
  gestureEnabled: true,
})

// Inherit from parent
useScreenGesture("parent", {
  gestureEnabled: true,
})

// Use root navigator
useScreenGesture("root", {
  gestureEnabled: true,
})

// Target specific ancestor
useScreenGesture({ ancestor: 2 }, {
  gestureEnabled: true,
})
Same applies to useScreenAnimation().

Step 8: Surface Slots (Optional)

If you need custom surface layers, use the new surface slot.
const customInterpolator = ({ progress }) => ({
  content: {
    style: { opacity: interpolate(progress, [0, 1], [0, 1]) },
  },
  backdrop: {
    style: { opacity: interpolate(progress, [0, 1], [0, 0.5]) },
  },
  surface: {  // NEW: Custom surface layer
    style: {
      backgroundColor: interpolate(
        progress,
        [0, 1],
        ["transparent", "rgba(0,0,0,0.1)"]
      ),
    },
    props: { pointerEvents: "none" },
  },
});

<Transition.Stack
  screenStyleInterpolator={customInterpolator}
  surfaceComponent={({ style, ...props }) => (
    <View {...props} style={[style, { borderRadius: 20 }]} />
  )}
>
  {/* screens */}
</Transition.Stack>

Step 9: Boundary Component Factory

If wrapping custom components, use the new factory.
import { Transition } from 'react-native-screen-transitions';

const MyCustomCard = ({ title, children, ...props }) => (
  <View {...props}>
    <Text>{title}</Text>
    {children}
  </View>
);

export const SharedCard = Transition.createBoundaryComponent(MyCustomCard, {
  alreadyAnimated: false,
});

// Usage
<SharedCard id="card-1" title="My Card">
  Content here
</SharedCard>

Migration Checklist

Work through this checklist to ensure complete migration:
  • Replace sharedBoundTag with Transition.Boundary.View/Pressable and id prop
  • Convert flat contentStyle/backdropStyle to slot format content: { style }/backdrop: { style }
  • Replace Transition.MaskedView wrapper with navigationMaskEnabled prop
  • Update dynamic snap points to use "auto" where appropriate
  • Change expandViaScrollView boolean to sheetScrollGestureBehavior string
  • Add new gesture velocity options if needed (gestureReleaseVelocityScale, gestureReleaseVelocityMax)
  • Update gesture hooks to use ancestor targeting (if applicable)
  • Test all animations on both iOS and Android
  • Verify shared elements work with new boundary components
  • Check snap point behavior with scroll coordination
  • Profile performance on low-end devices

Common Patterns

Sheet with Auto-Sizing

// Old pattern
<Transition.Modal
  snapPoints={[300, 600]}
  expandViaScrollView={true}
>
  <ScrollView>{/* content */}</ScrollView>
</Transition.Modal>

// New pattern
<Transition.Modal
  snapPoints={["auto", 1]}
  sheetScrollGestureBehavior="expand-and-collapse"
  screenStyleInterpolator={({ progress, layouts }) => ({
    content: {
      style: {
        transform: [{
          translateY: interpolate(progress, [0, 1], [
            layouts.content?.height ?? 300,
            0,
          ]),
        }],
      },
    },
  })}
>
  <ScrollView>{/* content */}</ScrollView>
</Transition.Modal>

Shared Element with Bounds

// Old pattern
<Transition.View sharedBoundTag="avatar">
  <Image {...} />
</Transition.View>

// New pattern
<Transition.Boundary.View id="avatar">
  <Image {...} />
</Transition.Boundary.View>

Masked Animation

// Old pattern
<Transition.MaskedView>
  <Transition.Stack screenStyleInterpolator={interpolator}>
    {/* screens */}
  </Transition.Stack>
</Transition.MaskedView>

// New pattern
<Transition.Stack
  navigationMaskEnabled
  screenStyleInterpolator={interpolator}
>
  {/* screens */}
</Transition.Stack>

Testing After Migration

After migrating, test:
  1. Animations: All transitions work smoothly
  2. Gestures: Swipe/drag works as expected
  3. Shared elements: Bounds animate correctly
  4. Snap points: Sheets snap to correct positions
  5. Scroll coordination: ScrollView + sheet gestures don’t conflict
  6. Platforms: Test on both iOS and Android
  7. Devices: Test on low-end devices for performance

Troubleshooting Migration

Animations not running

Ensure:
  • screenStyleInterpolator is set
  • Interpolator returns proper slot format
  • Boundaries are mounted and visible

Shared elements not animating

Check:
  • Boundary component IDs match (source and destination)
  • Both boundaries are rendered
  • Interpolator queries bounds with correct ID

Scroll and gesture conflict

Verify:
  • sheetScrollGestureBehavior is set
  • Transition.ScrollView is used (or manual coordination)
  • ScrollView is at boundary when testing

Getting Help

If you encounter issues during migration:
  1. Check this guide’s troubleshooting section
  2. Review the whats-new guide
  3. See specific concept guides for detailed explanations
  4. Check the GitHub repository for examples

Summary

v3.4 migration is straightforward:
  1. Update boundary components
  2. Convert interpolator return format
  3. Enable navigation masking
  4. Use auto snap points for dynamic content
  5. Update scroll gesture behavior
All old APIs still work, so you can migrate incrementally. This guide follows the recommended migration order for minimal disruption.

Next Steps