|
| 1 | +# Animating elements between screens |
| 2 | + |
| 3 | +This guide covers how to animate elements between screens. This feature is known as a [Shared Element Transition](https://docs.swmansion.com/react-native-reanimated/docs/api/sharedElementTransitions) and it's implemented in the [`@react-navigation/native-stack`](<(/docs/native-stack-navigator)>) with [React Native Reanimated](https://docs.swmansion.com/react-native-reanimated/). |
| 4 | + |
| 5 | +> Note: As of writing this guide, Shared Element Transitions are considered an experimental feature not recommended for production use. |
| 6 | +
|
| 7 | +<div style={{ display: 'flex', margin: '16px 0' }}> |
| 8 | + <video playsInline autoPlay muted loop> |
| 9 | + <source src="/assets/shared-element-transitions/shared-element-transitions.mp4" /> |
| 10 | + </video> |
| 11 | +</div> |
| 12 | + |
| 13 | +## Pre-requisites |
| 14 | + |
| 15 | +Before continuing this guide make sure your app meets these criteria: |
| 16 | + |
| 17 | +- You are using [`@react-navigation/native-stack`](/docs/native-stack-navigator). The Shared Element Transitions feature isn't supported in JS-based [`@react-navigation/stack`](/docs/stack-navigator). |
| 18 | +- You have [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/installation) **v3.0.0 or higher** installed and configured. |
| 19 | + |
| 20 | +## Minimal example |
| 21 | + |
| 22 | +To create a shared transition: |
| 23 | + |
| 24 | +1. Use `Animated` components imported from `react-native-reanimated`. |
| 25 | +2. Assign the same `sharedTransitionTag` to elements on different screens. |
| 26 | +3. Navigate between screens. The transition will start automatically. |
| 27 | + |
| 28 | +```jsx |
| 29 | +import * as React from 'react'; |
| 30 | +import { View, Button, StyleSheet } from 'react-native'; |
| 31 | +import { NavigationContainer } from '@react-navigation/native'; |
| 32 | +import { createNativeStackNavigator } from '@react-navigation/native-stack'; |
| 33 | + |
| 34 | +import Animated from 'react-native-reanimated'; |
| 35 | + |
| 36 | +// highlight-next-line |
| 37 | +const Stack = createNativeStackNavigator(); |
| 38 | + |
| 39 | +function HomeScreen({ navigation }) { |
| 40 | + return ( |
| 41 | + <View style={styles.container}> |
| 42 | + <Button |
| 43 | + title="Go to Details" |
| 44 | + onPress={() => navigation.navigate('Details')} |
| 45 | + /> |
| 46 | + <Animated.Image |
| 47 | + source={{ uri: 'https://picsum.photos/id/39/200' }} |
| 48 | + style={{ width: 300, height: 300 }} |
| 49 | + // highlight-next-line |
| 50 | + sharedTransitionTag="tag" |
| 51 | + /> |
| 52 | + </View> |
| 53 | + ); |
| 54 | +} |
| 55 | + |
| 56 | +function DetailsScreen({ navigation }) { |
| 57 | + return ( |
| 58 | + <View style={styles.container}> |
| 59 | + <Button title="Go back" onPress={() => navigation.goBack()} /> |
| 60 | + <Animated.Image |
| 61 | + source={{ uri: 'https://picsum.photos/id/39/200' }} |
| 62 | + style={{ width: 100, height: 100 }} |
| 63 | + // highlight-next-line |
| 64 | + sharedTransitionTag="tag" |
| 65 | + /> |
| 66 | + </View> |
| 67 | + ); |
| 68 | +} |
| 69 | + |
| 70 | +export default function App() { |
| 71 | + return ( |
| 72 | + <NavigationContainer> |
| 73 | + <Stack.Navigator> |
| 74 | + <Stack.Screen name="Home" component={HomeScreen} /> |
| 75 | + <Stack.Screen name="Details" component={DetailsScreen} /> |
| 76 | + </Stack.Navigator> |
| 77 | + </NavigationContainer> |
| 78 | + ); |
| 79 | +} |
| 80 | + |
| 81 | +const styles = StyleSheet.create({ |
| 82 | + container: { |
| 83 | + flex: 1, |
| 84 | + alignItems: 'center', |
| 85 | + }, |
| 86 | +}); |
| 87 | +``` |
| 88 | + |
| 89 | +`sharedTransitionTag` is a string that has to be unique in the context of a single screen, but has to match elements between screens. This prop allows Reanimated to identify and animate the elements, similarly to the [`key`](https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key) property, which tells React which element in the list is which. |
| 90 | + |
| 91 | +## Customizing the transition |
| 92 | + |
| 93 | +By default, the transition animates the `width`, `height`, `originX`, `originY` and `transform` properties using `withTiming` with a 500 ms duration. You can easily customize `width`, `height`, `originX`, and `originY` props. Customizing `transform` is also possible but it's far beyond the scope of this guide. |
| 94 | + |
| 95 | +> Note: Custom SharedTransition API is not finalized and might change in a future release. |
| 96 | +
|
| 97 | +To customize the transition you need to pass all the properties besides `transform`. |
| 98 | + |
| 99 | +```jsx |
| 100 | +import { SharedTransition } from 'react-native-reanimated'; |
| 101 | + |
| 102 | +const customTransition = SharedTransition.custom((values) => { |
| 103 | + 'worklet'; |
| 104 | + return { |
| 105 | + height: withSpring(values.targetHeight), |
| 106 | + width: withSpring(values.targetWidth), |
| 107 | + originX: withSpring(values.targetOriginX), |
| 108 | + originY: withSpring(values.targetOriginY), |
| 109 | + }; |
| 110 | +}); |
| 111 | + |
| 112 | +function HomeScreen() { |
| 113 | + return ( |
| 114 | + <Animated.Image |
| 115 | + style={{ width: 300, height: 300 }} |
| 116 | + sharedTransitionTag="tag" |
| 117 | + // highlight-next-line |
| 118 | + sharedTransitionStyle={customTransition} // add this to both elements on both screens |
| 119 | + /> |
| 120 | + ); |
| 121 | +} |
| 122 | +``` |
| 123 | + |
| 124 | +## Reference |
| 125 | + |
| 126 | +You can find a full Shared Element Transitions reference in the [React Native Reanimated documentation](https://docs.swmansion.com/react-native-reanimated/docs/api/sharedElementTransitions). |
| 127 | + |
| 128 | +## Alternatives |
| 129 | + |
| 130 | +Alternatively, you can use [`react-native-shared-element`](https://github.com/IjzerenHein/react-native-shared-element) library with a [React Navigation binding](https://github.com/IjzerenHein/react-navigation-shared-element) which implements Shared Element Transitions in a JS-based `@react-navigation/stack` navigator. This solution, however, isn't actively maintained. |
| 131 | + |
| 132 | +The [`react-native-navigation`](https://github.com/wix/react-native-navigation) also comes with support for Shared Element Transitions. You can read more about it [here](https://wix.github.io/react-native-navigation/docs/style-animations#shared-element-transitions). |
0 commit comments