This guide works for react-navigation-stack 2.0 (currently alpha)  as well as @react-navigation/stack 5.0 (part of React Navigation v5 alpha).

Have you ever wanted to implement a custom transition animation for React Navigation’s stack navigator? Then you’ve come to the right place. In this guide, we’ll learn how to implement a custom screen transition animation from scratch.

The guide assumes basic knowledge about animations in React Native, specifically interpolations. If you’re not familiar with interpolations, take a look at the official docs for Animated. Note that we’re going to use Reanimated in this guide since that’s what React Navigation uses, so you don’t need to read the rest of the docs for Animated.

Recently the Stack navigator was re-written to use Reanimated, and has a completely new API for defining transitions. It allows us to customize the transition for various parts of our screen as well as the header. The animations we define integrate seamlessly with the swipe gesture as well. We’ll implement a custom animation in the guide to get familiar with the concepts.

We are aiming for the following animation:

Before writing some code, let’s split the animation into small parts. During the animation/gesture, we can notice the following:

  • The focused screen moves and rotates
  • There’s a overlay behind the focused screen which gets darker/lighter
  • The screen behind scales up/down
  • The header items cross-fade

Let’s take a look at the official docs for Stack Navigator animations. There are 4 animation related options:

  • gestureDirection
  • transitionSpec
  • cardStyleInterpolator
  • headerStyleInterpolator

We want the screen to respond to horizontal gestures, so we’ll set gestureDirection to horizontal. For transitionSpec, we need to specify the configuration to use for the transition (e.g. spring animation and some options for it). To keep things simple, let’s use one of the built-in configs for this. For the header, we’ll use the built-in fade animation. So for now, our custom transition looks like this:

To use this configuration in our navigator, we can pass it in options (React Navigation v5):

Or navigationOptions (Stack v2 with React Navigation v4):

To use the animation for all screens, we can pass it in screenOptions (React Navigation v5) or defaultNavigationOptions (Stack v2 with React Navigation v4).

Now, let’s get to the interesting parts, cardStyleInterpolator and headerStyleInterpolator. So what are these?

The API for transitions is based on the concept of interpolations. If you have some math background, the interpolation idea should ring a bell and it’s exactly what you should remember from your math classes. React Navigation gives us the animated nodes for the current screen in the stack (current.progress) and screen after that one (next.progress). The progress values are the animation/gesture progress. When the screen in first starts opening, the value will be 0 and when it’s fully open, the value will be 1. We can interpolate on those and specify styles for various parts of the screen.

So let’s write the styles for the card (the part of the screen which contains the content) first. We want our focused card to rotate and translate based on the progress value of the card.

Here, the card starts from a translate value of screen width and 1 radian rotation to no translate and zero rotation.

We also want the previous card to scale down. If we have a next value, we know that the card is behind another card, so we need to scale it down based on the next card’s progress:

Next part is the overlay behind the card. It’s black by default, so we just need to change its opacity:

Now let’s put it all together in our custom transition under cardStyleInterpolator. The styles for the card will be in cardStyle and for overlay, it’ll be overlayStyle:

And we’re done! You can check the complete demo at the snack below and run it on your device:

https://snack.expo.io/@satya164/custom-animation-in-react-navigation-5

We have only implemented the card animation here, but the same principles apply for the header as well. For more details on what options are available, check the official docs.

Do you have any questions? Drop a comment below or tweet to me @satya164. Can’t wait to see what animations you come up with!

Need help with React Native?

Hire us

1 Comment

  • mmhggn October 28, 2019

    Is there a reason you use “Animated.interpolate(current.progress, …” instead of “current.progress.interpolate({…” for your animations?