Shared Element Transition

Android and iOS both support shared element transitions, where you can smoothly animate between related Views across two scenes. The Navigation router exposes this functionality to React using the SharedElement component. To identify a pair of shareable elements, you wrap each one in a SharedElement component and give them both the same name.

Note

On Android, shared element transitions disable the predictive back gesture

In our email example app, we smooth out the transition when a user opens an email by visually connecting the subject line on the 'inbox' scene with the open email on the 'mail' scene. We wrap the views on each scene inside a SharedElement component and give them both the same name.

// The 'inbox' scene
<Pressable
  onPress={() => {
    stateNavigator.navigate('mail', {id: id});
  }}>
  <SharedElement name={'email' + id}>
    <Text>Meeting invite</Text>
  </SharedElement>
</Pressable>
// The 'mail' scene
<SharedElement name={'email' + id}>
  <Text>Meeting invite</Text>
  <Text>Please come along to find out more about...</Text>
</SharedElement>

When you navigate between two States, the Navigation router calls the function you assign to the sharedElements prop of the destination Scene component. You return the shared name of the views that will participate in the transition. Return an array of names if sharing multiple elements. In our email example, we return the name we gave to the selected email.

<Scene stateKey="mail" sharedElements={data => 'email' + data.id}><Mail /></Scene>

On Android, there's an untidy overlap as the outgoing subject line turns into the incoming email. We neaten it up by setting the fadeMode prop to 'through'. This fades out the subject and fades in the email. We apply the prop to both SharedElement components so that the fade reverses when the user returns to their inbox.

<SharedElement name={'email' + id} fadeMode="through">

Not all custom animations work with shared element transitions. On Android, try the 'elevationScale' or 'hold' transitions on the the outgoing scene.

<Scene stateKey="inbox" crumbStyle={{type: 'elevationScale'}}><Inbox /></Scene>

Opening Sheets on iOS

Fluid shared transitions also work when opening sheets on iOS. You specify the name in the 'sharedElement' prop of the Sheet. You don't need to wrap the target view in a SharedElement because iOS fluid transitions target the whole sheet. In our email example, when a user taps a sender's icon we bring up their details in a sheet. We wrap the icon in a SharedElement so that iOS zooms the sheet out from the sender button.

<Button onPress={() => setDetent('expanded')}>
  <SharedElement name="sender">
    <Image />
  </SharedElement>
</Button>
<Sheet sharedElement="sender" detent={detent}>
</Sheet>