Skip to main content
The useScreenOptions hook provides a convenient way to generate React Navigation screen options that automatically adapt to the current theme and platform. It returns a configuration object optimized for native feel with blur effects on iOS and solid backgrounds on Android and web.

Usage

import { useScreenOptions } from "@/hooks/useScreenOptions";

export function WorkoutScreen() {
  const screenOptions = useScreenOptions();

  return (
    <Stack.Screen
      name="Workout"
      options={screenOptions}
    />
  );
}

Parameters

transparent
boolean
default:true
Controls whether the header should be transparent. When true, the header will use blur effects (on iOS) or transparent backgrounds. When false, the header will have a solid background color.
// Transparent header with blur (default)
const options = useScreenOptions();

// Solid header background
const options = useScreenOptions({ transparent: false });

Return Value

Returns a NativeStackNavigationOptions object with the following properties:
headerTitleAlign
'center'
required
Aligns the header title to the center of the navigation bar.
headerTransparent
boolean
required
Controls header transparency. Set to the value of the transparent parameter (defaults to true).
headerBlurEffect
'light' | 'dark'
required
The blur effect style for iOS headers. Automatically set to "dark" in dark mode and "light" in light mode.
headerTintColor
string
required
The color for header text and icons. Automatically set to theme.text to match the current theme.
headerStyle
object
required
Platform-specific header background styles:
  • iOS: undefined (allows blur effect to show through)
  • Android: theme.backgroundRoot (solid background)
  • Web: theme.backgroundRoot (solid background)
gestureEnabled
boolean
required
Enables swipe-back gesture navigation. Always set to true.
gestureDirection
'horizontal'
required
Sets the direction of swipe gestures to horizontal (left-to-right for back navigation).
fullScreenGestureEnabled
boolean
required
Controls full-screen gesture support. Automatically disabled when liquid glass effect is available, enabled otherwise.
contentStyle
object
required
Styles for the screen content container. Sets backgroundColor to theme.backgroundRoot for consistent theming.

Platform-Specific Behavior

On iOS, the hook creates a native-feeling navigation experience with:
  • Transparent headers with blur effects that adapt to light/dark mode
  • Centered titles matching iOS design guidelines
  • Swipe-back gestures enabled for natural navigation
  • Theme-aware tint colors for navigation elements
{
  headerTransparent: true,
  headerBlurEffect: isDark ? "dark" : "light", // Native iOS blur
  headerStyle: { backgroundColor: undefined }, // Allow blur to show
}

Examples

Basic Stack Navigator

import { createNativeStackNavigator } from "@react-navigation/native-stack";
import { useScreenOptions } from "@/hooks/useScreenOptions";

const Stack = createNativeStackNavigator();

export function AppNavigator() {
  const screenOptions = useScreenOptions();

  return (
    <Stack.Navigator screenOptions={screenOptions}>
      <Stack.Screen name="Home" component={HomeScreen} />
      <Stack.Screen name="Workout" component={WorkoutScreen} />
      <Stack.Screen name="Profile" component={ProfileScreen} />
    </Stack.Navigator>
  );
}

Solid Header Background

import { useScreenOptions } from "@/hooks/useScreenOptions";

export function SettingsNavigator() {
  const solidHeaderOptions = useScreenOptions({ transparent: false });

  return (
    <Stack.Navigator screenOptions={solidHeaderOptions}>
      <Stack.Screen 
        name="Settings" 
        component={SettingsScreen}
        options={{ title: "Settings" }}
      />
    </Stack.Navigator>
  );
}

Mixed Screen Options

import { useScreenOptions } from "@/hooks/useScreenOptions";

export function MixedNavigator() {
  const defaultOptions = useScreenOptions(); // Transparent by default
  const solidOptions = useScreenOptions({ transparent: false });

  return (
    <Stack.Navigator screenOptions={defaultOptions}>
      {/* This screen uses transparent header with blur */}
      <Stack.Screen 
        name="Home" 
        component={HomeScreen}
      />
      
      {/* This screen overrides with solid header */}
      <Stack.Screen 
        name="Details" 
        component={DetailsScreen}
        options={solidOptions}
      />
    </Stack.Navigator>
  );
}

Custom Screen Options with Base

import { useScreenOptions } from "@/hooks/useScreenOptions";
import { NativeStackNavigationOptions } from "@react-navigation/native-stack";

export function WorkoutScreen() {
  const baseOptions = useScreenOptions();
  
  const customOptions: NativeStackNavigationOptions = {
    ...baseOptions,
    title: "My Workout",
    headerRight: () => <SaveButton />,
    headerLeft: () => <BackButton />,
  };

  return (
    <Stack.Screen 
      name="Workout" 
      options={customOptions}
    />
  );
}

Per-Screen Dynamic Options

import { useLayoutEffect } from "react";
import { useNavigation } from "@react-navigation/native";
import { useScreenOptions } from "@/hooks/useScreenOptions";

export function DynamicScreen() {
  const navigation = useNavigation();
  const screenOptions = useScreenOptions({ transparent: false });
  
  useLayoutEffect(() => {
    navigation.setOptions({
      ...screenOptions,
      title: "Dynamic Title",
      headerRight: () => <ActionButton />,
    });
  }, [navigation, screenOptions]);

  return <View>{/* Screen content */}</View>;
}

Nested Navigators

import { createNativeStackNavigator } from "@react-navigation/native-stack";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { useScreenOptions } from "@/hooks/useScreenOptions";

const Stack = createNativeStackNavigator();
const Tab = createBottomTabNavigator();

function TabNavigator() {
  return (
    <Tab.Navigator>
      <Tab.Screen name="Workouts" component={WorkoutsScreen} />
      <Tab.Screen name="Progress" component={ProgressScreen} />
    </Tab.Navigator>
  );
}

export function RootNavigator() {
  const screenOptions = useScreenOptions();

  return (
    <Stack.Navigator screenOptions={screenOptions}>
      <Stack.Screen 
        name="Main" 
        component={TabNavigator}
        options={{ headerShown: false }}
      />
      <Stack.Screen name="WorkoutDetail" component={WorkoutDetailScreen} />
    </Stack.Navigator>
  );
}

When to Use

Stack Navigators

Use for all stack navigators to ensure consistent, theme-aware navigation headers across your app.

Platform Optimization

Automatically handles platform-specific optimizations like iOS blur effects and Android solid backgrounds.

Theme Integration

Ensures navigation elements update automatically when the user switches between light and dark mode.

Native Feel

Provides gestures and visual effects that feel native on each platform without manual configuration.

Best Practices

Apply useScreenOptions at the navigator level using screenOptions prop for consistency:
const screenOptions = useScreenOptions();

<Stack.Navigator screenOptions={screenOptions}>
  {/* All screens inherit these options */}
</Stack.Navigator>
Individual screens can override specific options while preserving the base configuration:
<Stack.Screen 
  name="Special"
  options={{
    ...screenOptions,
    title: "Special Title",
    headerRight: () => <CustomButton />
  }}
/>
Use transparent headers for content that scrolls under the header (like lists), and solid headers for static content:
// Transparent for scrollable content
const listOptions = useScreenOptions({ transparent: true });

// Solid for forms and static screens
const formOptions = useScreenOptions({ transparent: false });
The hook automatically disables full-screen gestures when the liquid glass effect is available to prevent gesture conflicts:
fullScreenGestureEnabled: isLiquidGlassAvailable() ? false : true
This is handled automatically, but be aware when designing gesture-heavy interfaces.
The hook uses useTheme internally, so the navigation options automatically update when the system color scheme changes. You don’t need to manually handle theme updates.

useTheme

Access theme colors and dark mode state (used internally by useScreenOptions)

useColorScheme

Get the raw color scheme value

Build docs developers (and LLMs) love