Building the Animated Twitter Profile Header with React Native Header

codeherence
7 min readMar 30, 2023
The Twitter profile header in a non-collapsed and collapsed state.

Following the release of our new library, react-native-header, which enables developers to create cross-platform animated headers in React Native, we set out to design a custom header that would:

  • Demonstrate the ease of using the library to build visually appealing headers
  • Enhance the library’s implementation

The Twitter profile header on the mobile application stands out for its deviation from the “native” feel, showcasing an impressive and aesthetically pleasing design. As a result, I selected it for this project. You can view the Twitter profile header in action via this GIPHY link, which provides a gif showcasing its features.

Setting up

The implementation of the header exists under the example folder of the react-native-header library.

Let’s begin by downloading the repository onto your machine:

  1. Click here and press on the button “Create fork” to fork the repository.

2. Once the repository is forked, navigate to the fork and clone it on your machine by opening a terminal and running

git clone https://github.com/<YOUR_USERNAME>/react-native-header.git

and replacing <YOUR_USERNAME> with your GitHub username. If you’re having difficulty forking and cloning the repository, visit the GitHub documentation on how to fork a repo.

Running the application

Once you have cloned the repository onto your machine, run the following to install the dependencies in the root folder:

cd react-native-header && yarn

Then, install the dependencies for the example Expo application:

cd example && yarn

If you’re testing the application on an iOS device/simulator, make sure to install the Pod dependencies after:

npx pod-install

Then, we can run the application by opening 2 terminals and executing:

yarn start

in one of them, and

yarn run ios

in the other if you are running the application on an iOS device/simulator, or

yarn run android

if you are running it on an Android device/emulator.

Let’s Analyze

The main file that implements the Twitter profile header can be found at: example/src/screens/usage/TwitterProfile.tsx

What the example looks like on an iPhone 14 Pro Max simulator

When we tap the button labeled “Twitter Profile” in the application, we are presented with the mock Twitter profile header.

This screen consists of several components:

  • TwitterProfile — The primary component responsible for rendering all elements on the screen.
  • LargeHeaderComponent — This component encompasses the large header, starting from the user’s name, extending to the row displaying the user’s followers, and showcasing up to three preview images.
  • HeaderComponent — Positioned at the top of the screen, this component comprises the navigation buttons and the navigation header, which becomes visible as the user scrolls upward. Additionally, the component features a banner image for the Twitter profile that dynamically blurs upon scrolling.
  • SomeComponent and MemoizedComponent — these components are only used to fill up the list so we can test the scrolling animations on our headers. Ignore these components.

The Header

Within the header, we implement a majority of the animations showcased in the GIPHY link. These animations are primarily influenced by the scroll position of the list, resulting in a smooth and interactive user experience.

const HeaderComponent: React.FC<ScrollHeaderProps> = ({ showNavBar, scrollY }) => {
// ...
};

We started by defining a functional React component named HeaderComponent, which accepts the ScrollHeaderProps type as its props. The props include showNavBar, which determines whether the navigation bar children should be shown or not, and scrollY, a reanimated shared value that represents the scroll position on the Y-axis. The showNavBar and scrollY values are provided by the library itself to the HeaderComponent and LargeHeaderComponent props of the SectionListWithHeaders.

Now, we have a few things to animate:

  1. The blurring of the banner when the scrollY value is not 0 (i.e., the user has scrolled up or down)
  2. The scaling of the banner on iOS when the list is pulled down
  3. The upwards translation of the banner when the user scrolls up, until it matches the height of the smaller header
  4. The profile image’s scaling and translation
  5. The change of the zIndex of the profile image & follow button so that it goes behind the header bar while the user scrolls up

We begin with the blurring of the banner (1):

const blurOpacity = useDerivedValue(() => {
return interpolate(
Math.abs(scrollY.value),
[0, 40],
[0, 1],
Extrapolate.CLAMP
);
});

Here, we create a derived value using useDerivedValue to calculate the blur opacity of the banner based on the scroll position. The interpolate function maps the scrollY value to an opacity value between 0 and 1, enabling the blur effect to appear as the user scrolls up or down.

The BlurView animating in and out based on the scroll position

The scaling of the banner (2) was achieved through a convenience component, ScalingView, that is exported from react-native-header:

<ScalingView
scrollY={scrollY}
translationDirection="none"
// These two values were arbitrarily chosen.
// Will need to adjust scaling view to allow for scale translations to separate
// x, y and z.
endScale={6}
endRange={height * 0.43}
>
{/* ... Banner Image & BlurView ... */}
</ScalingView>

The upwards translation of the profile banner (3) was simply done by:

const bannerTranslation = useDerivedValue(() => {
return interpolate(
scrollY.value,
[0, BANNER_BOTTOM_HEIGHT_ADDITION],
[0, -BANNER_BOTTOM_HEIGHT_ADDITION],
Extrapolate.CLAMP
);
});

and the profile image’s scaling and translation (4):

const profileImageScale = useDerivedValue(() => {
return interpolate(
scrollY.value,
[0, AVATAR_SIZE_VALUE],
[AVATAR_START_SCALE, AVATAR_END_SCALE],
Extrapolate.CLAMP
);
});

const profileImageTranslationY = useDerivedValue(() => {
return interpolate(
profileImageScale.value,
[AVATAR_START_SCALE, AVATAR_END_SCALE],
[0, AVATAR_SIZE_VALUE / 2],
Extrapolate.CLAMP
);
});

Here, profileImageScale calculates the scaling factor for the profile image based on the scroll position (scrollY). When the user scrolls up, the profile image will gradually scale down, and when the user scrolls down, the profile image will scale up.

profileImageTranslationY calculates the translation value for the profile image on the Y-axis based on the profileImageScale. As the profile image scales down, we want to ensure that it appears to stay in the same position relative to the bottom. To achieve this, we use the interpolate function to map the profileImageScale value to a range of translation values (0 to AVATAR_SIZE_VALUE / 2).

As the profile image scales down (scrolling up), it will also translate upwards by half of the AVATAR_SIZE_VALUE, which compensates for the scaling effect and gives the impression that the image is becoming smaller without moving from its position relative to the bottom.

Finally, we implement the zIndex modifier on the profile image & follow button container (5):

const rootProfileRowZIndexStyle = useAnimatedStyle(() => {
return {
zIndex: profileImageScale.value <= AVATAR_END_SCALE ? -1 : 1,
};
});

This code ensures that the profile image hides behind the header once it has been scaled down.

In this tutorial, we are not going to go through the JSX. Now that you have an understanding of the animation values and what they’re being used for, feel free to dig into the header code!

The Large Header

The large header implementation is not exactly very interesting, since all it does is define the views between the name of the user and their follower statistics via simple React Native components.

Conclusion

In this blog post, we explored the implementation of a Twitter profile header that showcases a range of dynamic and interactive features such as a blurred banner view on scroll, a scaling banner on pull-down, and a profile image that scales down and hides behind the header while scrolling up. By leveraging the power of the react-native-header library, we were able to eliminate the need for a significant amount of boilerplate code to achieve the following:

  1. Expose the scroll position: Instead of writing our own custom code to track and update the scroll position, react-native-header provides us with an efficient and straightforward way to access the current scroll position (scrollY) and use it for calculations in our animations.
  2. Expose whether or not to show the header navigation bar’s children: The library offers an easy method to determine when to show or hide specific elements in the navigation bar, such as icons and text, depending on the scroll position.
  3. Integrate everything into a SectionListWithHeaders: With react-native-header, we can seamlessly combine all the aforementioned features and animations into a single SectionListWithHeaders component that offers a smooth and interactive user experience.

By using react-native-header, react-native-reanimated and the techniques discussed in this blog post, developers can create engaging, performant and visually appealing user interfaces for their mobile applications. The Twitter profile header serves as a great example of how we can use the library to simplify the development process and deliver high-quality, interactive, and visually captivating experiences to our users.

Get in touch

If you or your organization are seeking to develop highly interactive and high-performance cross-platform applications, we invite you to connect with us and explore how our expertise can benefit your project.

Follow me

If you would like to see more content like this, make requests on the type of content you want to see, etc., feel free to:

  1. Follow my Twitter
  2. Join my Discord
  3. Follow me on LinkedIn

--

--