managarten/manadeck/apps/mobile/Docs/HEADER_STYLING_GUIDE.md
Till-JS e7f5f942f3 chore: initial commit - consolidate 4 projects into monorepo
Projects included:
- maerchenzauber (NestJS backend + Expo mobile + SvelteKit web + Astro landing)
- manacore (Expo mobile + SvelteKit web + Astro landing)
- manadeck (NestJS backend + Expo mobile + SvelteKit web)
- memoro (Expo mobile + SvelteKit web + Astro landing)

This commit preserves the current state before monorepo restructuring.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-22 23:38:24 +01:00

7.9 KiB

Header Styling Guide - Expo Router

Overview

This guide covers all available styling options for headers in Expo Router's Stack navigator.

Available Header Style Options

Basic Configuration

import { Stack } from 'expo-router';
import { useThemeColors } from '~/utils/themeUtils';

export default function Layout() {
  const colors = useThemeColors();

  return (
    <Stack
      screenOptions={{
        // Header visibility
        headerShown: true,

        // Header title
        title: 'My Screen',

        // Glass effect (iOS only)
        headerTransparent: true,
        headerBlurEffect: 'systemMaterial',

        // Colors
        headerTintColor: colors.foreground,
        headerStyle: {
          backgroundColor: colors.background,
        },

        // Title styling
        headerTitleStyle: {
          fontWeight: 'bold',
          fontSize: 18,
          fontFamily: 'System',
        },

        // Shadow/Border
        headerShadowVisible: false,

        // Back button
        headerBackVisible: true,
        headerBackTitle: 'Back',
        headerBackTitleVisible: true,
      }}>
      <Stack.Screen name="index" />
    </Stack>
  );
}

Core Style Properties

1. headerStyle

Controls the header container's style.

headerStyle: {
  backgroundColor: '#ff0000',  // Background color (not needed with headerTransparent)
}

Note: Cannot set height - this is controlled by the OS.

2. headerTintColor

Sets the color for back button and title text.

headerTintColor: '#ffffff'  // White text and icons

3. headerTitleStyle

Customizes the title text appearance.

headerTitleStyle: {
  fontWeight: 'bold',
  fontSize: 20,
  fontFamily: 'CustomFont',
  color: '#000000',  // Can override headerTintColor for title only
}

4. headerTitleAlign

Controls title alignment.

headerTitleAlign: 'center' | 'left'  // Default: 'center' on iOS, 'left' on Android

5. headerTransparent

Makes header transparent for glass effect.

headerTransparent: true  // Required for headerBlurEffect

6. headerBlurEffect (iOS only)

Adds blur/glass effect to header.

headerBlurEffect: 'systemMaterial' | 'regular' | 'prominent' |
                  'systemThinMaterial' | 'systemUltraThinMaterial' |
                  'systemChromeMaterial' | 'systemThinMaterialLight' |
                  'systemThinMaterialDark'

7. headerShadowVisible

Controls header shadow/border visibility.

headerShadowVisible: false  // Remove shadow

Back Button Customization

headerBackVisible

Show/hide back button.

headerBackVisible: true  // Show back button

headerBackTitle (iOS only)

Custom text for back button.

headerBackTitle: 'Home'  // Default: Previous screen title

headerBackTitleVisible (iOS only)

Show/hide back button text.

headerBackTitleVisible: false  // Only show arrow icon

headerBackImageSource

Custom back button icon.

headerBackImageSource: require('./assets/back-icon.png')

Title Customization

headerTitle

Function to render custom title component.

headerTitle: () => (
  <View>
    <Text style={{ fontSize: 20, fontWeight: 'bold' }}>Custom Title</Text>
  </View>
)

headerTitleAlign

headerTitleAlign: 'center'  // 'center' | 'left'

Header Buttons

headerLeft

Custom left button/component.

headerLeft: () => (
  <Pressable onPress={() => console.log('Left button')}>
    <Icon name="menu" size={24} />
  </Pressable>
)

headerRight

Custom right button/component.

headerRight: () => (
  <Pressable onPress={() => console.log('Right button')}>
    <Icon name="settings" size={24} />
  </Pressable>
)

Search Bar (iOS 11+)

headerSearchBarOptions

Native iOS search bar in header.

headerSearchBarOptions: {
  placeholder: 'Search...',
  onChangeText: (text) => console.log(text),
  hideWhenScrolling: true,
}

Large Title (iOS)

headerLargeTitle

Large title that collapses on scroll (iOS only).

headerLargeTitle: true

headerLargeTitleStyle

Style for large title.

headerLargeTitleStyle: {
  fontWeight: 'bold',
  fontSize: 34,
}

headerLargeTitleShadowVisible

Shadow for large title.

headerLargeTitleShadowVisible: false

Complete Example: Glass Header with Theme

import { Stack } from 'expo-router';
import { Platform } from 'react-native';
import { useThemeColors } from '~/utils/themeUtils';

export default function Layout() {
  const colors = useThemeColors();

  return (
    <Stack
      screenOptions={{
        headerShown: true,

        // Glass effect (iOS only)
        ...(Platform.OS === 'ios' ? {
          headerTransparent: true,
          headerBlurEffect: 'systemMaterial',
        } : {
          headerStyle: {
            backgroundColor: colors.background,
          },
        }),

        // Colors
        headerTintColor: colors.foreground,

        // Title styling
        headerTitleStyle: {
          fontWeight: '600',
          fontSize: 17,
        },
        headerTitleAlign: 'center',

        // Shadow
        headerShadowVisible: false,

        // Back button
        headerBackTitleVisible: false,
      }}>
      <Stack.Screen
        name="index"
        options={{
          title: 'Home',
          headerRight: () => (
            <Pressable onPress={() => console.log('Settings')}>
              <Icon name="settings" size={24} color={colors.foreground} />
            </Pressable>
          ),
        }}
      />
    </Stack>
  );
}

Platform Differences

iOS

  • Default title alignment: center
  • Supports headerBlurEffect
  • Supports headerLargeTitle
  • Supports headerSearchBarOptions
  • Back button shows previous screen title by default

Android

  • Default title alignment: left
  • No blur effect (use solid backgroundColor)
  • No large title support
  • No native search bar in header
  • Back button only shows arrow icon

Common Patterns

1. Remove Header Shadow

headerShadowVisible: false

2. Custom Back Button

headerLeft: () => (
  <Pressable onPress={() => router.back()}>
    <Icon name="arrow-back" size={24} color={colors.foreground} />
  </Pressable>
)

3. Hide Header on Specific Screen

<Stack.Screen
  name="fullscreen"
  options={{ headerShown: false }}
/>

4. Dynamic Title

<Stack.Screen
  name="detail"
  options={{
    title: dynamicTitle,
    // or
    headerTitle: () => <CustomTitleComponent title={dynamicTitle} />
  }}
/>

5. Large Title with Search (iOS)

<Stack.Screen
  name="search"
  options={{
    headerLargeTitle: true,
    headerSearchBarOptions: {
      placeholder: 'Search items...',
    },
  }}
/>

Limitations

Cannot Customize:

  • Header height (controlled by OS)
  • Status bar appearance (use expo-status-bar package)
  • Exact positioning of elements (use headerLeft/headerRight instead)

Platform-Specific:

  • ⚠️ Blur effects only on iOS
  • ⚠️ Large titles only on iOS
  • ⚠️ Native search bar only on iOS 11+

Troubleshooting

Issue: Title not visible on transparent header

Solution: Ensure headerTintColor is set to a visible color

Issue: Back button wrong color

Solution: Set headerTintColor - it controls both title and back button

Issue: Header too tall/short

Solution: Height is controlled by OS and cannot be changed. Use custom header component if needed.

Issue: Shadow visible on transparent header

Solution: Set headerShadowVisible: false

References