Mobile Development8 min read

Creating Custom Headers in Expo SDK 51 A Comprehensive Guide

Custom headers are essential for creating a unique and branded mobile experience. With Expo SDK 51 and React Navigation 6, we have more powerful tools than ever to create beautiful, interactive headers. In this comprehensive guide, we'll explore everything from basic customization to advanced patterns.

Creating Custom Headers in Expo SDK 51 A Comprehensive Guide

Written by

Marwan Hisham

Published on

17 Jan 2024

Introduction

Custom headers are essential for creating a unique and branded mobile experience. With Expo SDK 51 and React Navigation 6, we have more powerful tools than ever to create beautiful, interactive headers. In this comprehensive guide, we'll explore everything from basic customization to advanced patterns.

Prerequisites

Before we dive in, make sure you have the following set up:

npx create-expo-app my-header-app
cd my-header-app
npx expo install @react-navigation/native @react-navigation/native-stack

Basic Header Customization

Let's start with the fundamentals of header customization:

import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { StyleSheet } from 'react-native';

type RootStackParamList = {
  Home: undefined;
  Details: { itemId: number };
};

const Stack = createNativeStackNavigator
<RootStackParamList>();

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator
        screenOptions={{
          headerStyle: styles.header,
          headerTintColor: '#fff',
          headerTitleStyle: styles.headerTitle,
        }}
      >
        <Stack.Screen
          name="Home"
          component={HomeScreen}
          options={{
            title: 'My App',
          }}
        />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

const styles = StyleSheet.create({
  header: {
    backgroundColor: '#6200ee',
  },
  headerTitle: {
    fontWeight: 'bold',
  },
});

Advanced Header Patterns

Custom Header Component

Here's how to create a completely custom header:

import { Image, Pressable, View } from "react-native";

interface CustomHeaderProps {
  title: string;
  onMenuPress: () => void;
  onSearchPress: () => void;
}

function CustomHeader({
  title,
  onMenuPress,
  onSearchPress,
}: CustomHeaderProps) {
  return (
    <View style={styles.headerContainer}>
      <Pressable onPress={onMenuPress} style={styles.iconButton}>
        <Image source={require("./assets/menu-icon.png")} style={styles.icon} />
      </Pressable>

      <Text style={styles.headerTitle}>{title}</Text>

      <Pressable onPress={onSearchPress} style={styles.iconButton}>
        <Image
          source={require("./assets/search-icon.png")}
          style={styles.icon}
        />
      </Pressable>
    </View>
  );
}

// Implementation in navigation
<Stack.Screen
  name="Home"
  component={HomeScreen}
  options={{
    header: ({ navigation, route, options })=> (
      <CustomHeader
        title="Home"
        onMenuPress={()=> navigation.openDrawer()}
        onSearchPress={()=> navigation.navigate("Search")}
      />
    ),
  }}
/>;

Animated Headers

Here's an example of a collapsible header:

import Animated, {
  useAnimatedScrollHandler,
  useAnimatedStyle,
  useSharedValue,
  withSpring,
} from "react-native-reanimated";

function AnimatedHeader() {
  const scrollY = useSharedValue(0);

  const scrollHandler = useAnimatedScrollHandler({
    onScroll: (event) => {
      scrollY.value = event.contentOffset.y;
    },
  });

  const headerStyle = useAnimatedStyle(() => {
    return {
      height: withSpring(Math.max(45, 90 - scrollY.value), {
        damping: 20,
        stiffness: 90,
      }),
    };
  });

  return (
    <View style={styles.container}>
      <Animated.View style={[styles.header, headerStyle]}>
        <Text style={styles.headerText}>Animated Header</Text>
      </Animated.View>

      <Animated.ScrollView onScroll={scrollHandler} scrollEventThrottle={16}>
        {/* Content */}
      </Animated.ScrollView>
    </View>
  );
}

Handling Platform Differences

Different platforms have different header conventions. Here's how to handle them:

import { Platform } from "react-native";

const screenOptions = {
  headerStyle: {
    height: Platform.select({
      ios: 110,
      android: 90,
    }),
    ...Platform.select({
      ios: {
        shadowColor: "#000",
        shadowOffset: { width: 0, height: 2 },
        shadowOpacity: 0.2,
      },
      android: {
        elevation: 4,
      },
    }),
  },
};

Performance Optimization

Memoization

Always memoize your custom header components:

import { memo, useCallback } from "react";

const MemoizedHeader = memo(function Header({ title, onPress }) {
  const handlePress = useCallback(() => {
    onPress();
  }, [onPress]);

  return (
    <Pressable onPress={handlePress}>
      <Text>{title}</Text>
    </Pressable>
  );
});

Layout Animation

For smooth transitions:

import { LayoutAnimation } from "react-native";

function updateHeaderHeight() {
  LayoutAnimation.configureNext(
    LayoutAnimation.create(
      300,
      LayoutAnimation.Types.easeInEaseOut,
      LayoutAnimation.Properties.opacity,
    ),
  );
  setHeaderExpanded(!headerExpanded);
}

Common Pitfalls

  1. Safe Area Handling
import { SafeAreaView } from "react-native-safe-area-context";

function SafeHeader() {
  return (
    <SafeAreaView edges={["top"]} style={styles.safeArea}>
      <View style={styles.headerContent}>{/* Header content */}</View>
    </SafeAreaView>
  );
}
  1. Status Bar Management
import { StatusBar } from "expo-status-bar";

function Header() {
  return (
    <View style={styles.header}>
      <StatusBar style="light" />
      {/* Header content */}
    </View>
  );
}

Conclusion

Creating custom headers in Expo SDK 51 offers unlimited possibilities for customization. Remember to:

  • Consider platform-specific behaviors
  • Optimize performance through memoization
  • Handle safe areas and status bars appropriately
  • Test thoroughly on both iOS and Android

Resources


For the thumbnail, I recommend creating an image with:

  1. A mobile phone frame in the center
  2. A prominently displayed custom header with gradient background
  3. Some floating UI elements showing different header variations
  4. React Navigation's purple color scheme (#6200ee) as the primary color
  5. Dimensions: 1200x630px (optimal for social media sharing)

You can create this using design tools like Figma or Adobe XD, or commission it from a designer for the best results.