How I built a custom header for react navigation

·

4 min read

I want to share my insights on how to integrate a custom header in a React native application.

Why bother with custom headers anyway?

Differentiate yourself; don't be stuck in a Matrix. ESCAPE FROM THE MATRIX!!!11!.

Aside from the obvious Joke, there are many valid reasons why you would want to have Custom Header. Here are some of the more obvious Reasons:

  • Custom Design: If you have a specific design or branding requirement for your app, a custom header allows you to fully customize the appearance, layout, and behavior to match your desired look and feel.

  • Platform-Specific Considerations: If you are building a cross-platform app and want to have platform-specific headers that adhere to the design guidelines and behaviors of each platform (e.g., iOS and Android), a custom header can help achieve that consistency.

  • Enhanced Interactions: If you want to add interactive features to the header, like animations, gestures, or custom transitions, a custom header allows you to implement these functionalities more easily.

How do I start?

  1. Initialize your React Native App

     npx react-native init MyApp --template react-native-template-typescript
    
  2. Install the required dependencies

     cd MyApp
     npm install @react-navigation/native @react-navigation/native-stack
     npx pod-install
    

Create the header component

I have chosen to develop a straightforward header component for this blog, offering the flexibility to include Icons on either side and a central title. The component will have a transparent background.

Before moving forward with the implementation, it is important to note that this blog post does not provide a 1:1 Implementation guide, but rather presents a possible approach to creating a custom header. Its purpose is to provide guidance and help readers gain a better understanding of the implementation process.

type CustomHeaderProps = {
    leftIcon?: SvgProps;
    rightIcon?: SvgProps;
    title: string;
}

The icon component

To utilize our custom icons, we require a component that enables us to render our icons and assign click functionality to them.

import React from 'react';
import { TouchableOpacity } from 'react-native';
import Svg, { SvgProps } from 'react-native-svg';

interface IconProps extends SvgProps {
  onPress?: () => void;
}

const ClickableIcon: React.FunctionComponent<IconProps> = ({
  onPress,
  ...svgProps
}) => {
  return (
    <TouchableOpacity onPress={onPress}>
      <Svg {...svgProps} />
    </TouchableOpacity>
  );
};

With our types defined and all the necessary Components at our disposal, it's time to create a FunctionComponent for our CustomHeader. In this example, we will include two Icons that will serve as navigation buttons, directing the user to a page named "Friends" within our RootStackParams.

const CustomHeader: React.FunctionComponent<CustomHeaderProps> = ({ title, left, right }) => {
  const navigation = useNavigation();
  const handleSvgClick = () => {
    navigation.navigate('Friends', { uid: 1 })
  }

  const leftIcon = (leftIcon &&
    <ClickableIcon onPress={handleSvgClick}>
        {leftIcon}
    </ClickableIcon>
  );

  const rightIcon = (right &&
    <ClickableIcon onPress={handleSvgClick}>
        {rightIcon}
    </ClickableIcon>
  );

  return (
    <View>
      <>{leftIcon}</>
      <Text>{title}</Text>
      <>{rightIcon}</>
    </View>
  )
}

export default CustomHeader;

Additionally, we have encapsulated our icons within view tags to ensure that our title always stays centered, regardless of the presence or absence of icons.

We have also applied several other style rules to enhance the overall visual appeal of our fancy header.


...
  return (
    <View style={styles.container}>
      <View>{leftIcon}</View>
      <Text style={styles.text}>{title}</Text>
      <View>{rightIcon}</View>
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    backgroundColor: 'transparent',
    paddingHorizontal: 16,
    marginTop: 16,
    height: 80,
  },
  text: {
    color: 'white',
    textAlign: 'center',
    fontFamily: 'Lato-Bold',
    fontSize: 24,
  }
}

Telling the Stack Navigator about it

The react-navigation stack navigator needs to know what header to use, or it will fall back to the default one.

import { createStackNavigator } from '@react-navigation/stack';
import Header from '@components/CustomHeader';

const Stack = createStackNavigator();

function MyStack() {
  const friends = <MySvg/>
  return (
    <Stack.Navigator>
      <Stack.Screen 
        name="Home" 
        component={Home} 
        option={{
          header: () => (
            <CustomHeader title="Home" leftIcon={friends} rightIcon={friends} />
          ),
        }}
      />
    </Stack.Navigator>
  );
}

Full code snippet

import React from 'react';
import { TouchableOpacity } from 'react-native';
import Svg, { SvgProps } from 'react-native-svg';

type CustomHeaderProps = {
    leftIcon?: SvgProps;
    rightIcon?: SvgProps;
    title: string;
}

interface IconProps extends SvgProps {
  onPress?: () => void;
}

const ClickableIcon: React.FunctionComponent<IconProps> = ({
  onPress,
  ...svgProps
}) => {
  return (
    <TouchableOpacity onPress={onPress}>
      <Svg {...svgProps} />
    </TouchableOpacity>
  );
};

const CustomHeader: React.FunctionComponent<CustomHeaderProps> = ({ title, left, right }) => {
  const navigation = useNavigation();
  const handleSvgClick = () => {
    navigation.navigate('Friends', { uid: 1 })
  }

  const leftIcon = (leftIcon &&
    <ClickableIcon onPress={handleSvgClick}>
        {leftIcon}
    </ClickableIcon>
  );

  const rightIcon = (right &&
    <ClickableIcon onPress={handleSvgClick}>
        {rightIcon}
    </ClickableIcon>
  );

    return (
    <View style={styles.container}>
      <View>{leftIcon}</View>
      <Text style={styles.text}>{title}</Text>
      <View>{rightIcon}</View>
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    backgroundColor: 'transparent',
    paddingHorizontal: 16,
    marginTop: 16,
    height: 80,
  },
  text: {
    color: 'white',
    textAlign: 'center',
    fontFamily: 'Lato-Bold',
    fontSize: 24,
  }
}

export default CustomHeader

Conclusion

Custom headers are crucial for enhancing the user experience and visual appeal of mobile applications. Additionally, they can provide functionality like search bars or user profile information.