Expo UI and Router for iOS
Expo Router

Stack

Learn how to use the Stack navigator in Expo Router.

Using a Stack Navigator with Expo Router — Navigate between screens, pass params between screens, create dynamic routes, and configure the screen titles and animations.

A stack navigator is the foundational way of navigating between routes in an app. On Android, a stacked route animates on top of the current screen. On iOS, a stacked route animates from the right. Expo Router provides a Stack navigation component that creates a navigation stack and allows you to add new routes in your app.

This guide provides information on how you can create a Stack navigator in your project and customize an individual route's options and header.

Get started

You can use file-based routing to create a stack navigator. Here's an example file structure:

src

app

  _layout.tsx

  index.tsx

  details.tsx

This file structure produces a layout where the index route is the first route in the stack, and the details route is pushed on top of the index route when navigated.

You can use the src/app/_layout.tsx file to define your app's Stack navigator with these two routes:

import { Stack } from 'expo-router';

export default function Layout() {
  return <Stack />;
}

Screen options and header configuration

Starting in SDK 55, you can configure screen options and the header using either the options-based API or the new composition components API. Both APIs can be used interchangeably in your project.

Statically configure route options

You can use the <Stack.Screen name={routeName} /> component in the layout component route to statically configure a route's options.

import { Stack } from 'expo-router';

export default function Layout() {
  return (
    <Stack
      // See React Navigation documentation for more information on available screenOptions: https://reactnavigation.org/docs/headers/#sharing-common-options-across-screens
      screenOptions={{
        headerStyle: {
          backgroundColor: '#f4511e',
        },
        headerTintColor: '#fff',
        headerTitleStyle: {
          fontWeight: 'bold',
        },
      }}>
      {/* Optionally configure static options outside the route.*/}
      <Stack.Screen name="home" options={{}} />
    </Stack>
  );
}

Configure header bar

You can configure the header bar for all routes in a Stack navigator by using the screenOptions prop. This is useful for setting a common header style across all routes.

import { Stack } from 'expo-router';

export default function Layout() {
  return (
    <Stack
      screenOptions={{
        headerStyle: {
          backgroundColor: '#f4511e',
        },
        headerTintColor: '#fff',
        headerTitleStyle: {
          fontWeight: 'bold',
        },
      }}
    />
  );
}

Set screen options dynamically

To configure a route's options dynamically, you can use either the composition components or the options-based API.

Options API

import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
import { View, Text, StyleSheet } from 'react-native';

export default function Details() {
  const router = useRouter();
  const params = useLocalSearchParams();

  return (
    <View style={styles.container}>
      <Stack.Screen
        options={{
          title: params.name,
          headerStyle: { backgroundColor: 'lightblue' },
        }}
      />
      <Text
        onPress={() => {
          router.setParams({ name: 'Updated' });
        }}>
        Update the title
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
});

Composition components

Screen composition API is in alpha and available in SDK 55 and later.

import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
import { View, Text, StyleSheet } from 'react-native';

export default function Details() {
  const router = useRouter();
  const params = useLocalSearchParams();

  return (
    <View style={styles.container}>
      <Stack.Title>{params.name}</Stack.Title>
      <Stack.Header style={{ backgroundColor: 'lightblue' }} />
      <Text
        onPress={() => {
          router.setParams({ name: 'Updated' });
        }}>
        Update the title
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
});

Available header options

The Stack navigator supports comprehensive header configuration options. Below are all the header-related options available:

Header options

OptionPlatformDescription
headerAndroid, iOSCustom header to use instead of the default header. This accepts a function that returns a React Element to display as a header. The function receives an object containing the following properties as the argument
  • navigation - The navigation object for the current screen.
  • route - The route object for the current screen.
  • options - The options for the current screen
  • back - Options for the back button, contains an object with a title property to use for back button label.

. To set a custom header for all the screens in the navigator, you can specify this option in the screenOptions prop of the navigator. Note that if you specify a custom header, the native functionality such as large title, search bar etc. won't work. | | headerBackButtonDisplayMode | iOS | How the back button displays icon and title. Supported values|

  • "default" - Displays one of the following depending on the available space: previous screen's title, generic title (e.g. 'Back') or no title (only icon).
  • "generic" – Displays one of the following depending on the available space: generic title (e.g. 'Back') or no title (only icon).
  • "minimal" – Always displays only the icon without a title.

. The space-aware behavior is disabled when:

  • The iOS version is 13 or lower
  • Custom font family or size is set (e.g. with headerBackTitleStyle)
  • Back button menu is disabled (e.g. with headerBackButtonMenuEnabled)

. In such cases, a static title and icon are always displayed. | | headerBackButtonMenuEnabled | iOS | Boolean indicating whether to show the menu on longPress of iOS >= 14 back button. Defaults to true. | | headerBackground | Android, iOS | Function which returns a React Element to render as the background of the header. This is useful for using backgrounds such as an image or a gradient. | | headerBackIcon | Android, iOS | Icon to display in the header as the icon in the back button. Defaults to back icon image for the platform|

  • A chevron on iOS
  • An arrow on Android

. Currently only supports image sources. | | headerBackTitle | iOS | Title string used by the back button on iOS. Defaults to the previous scene's title, "Back" or arrow icon depending on the available space. See headerBackButtonDisplayMode to read about limitations and customize the behavior. Use headerBackButtonDisplayMode: "minimal" to hide it. | | headerBackTitleStyle | iOS | Style object for header back title. Supported properties|

  • fontFamily
  • fontSize

| | headerBackVisible | Whether the back button is visible in the header. You can use it to show a back button alongside headerLeft if you have specified it. This will have no effect on the first screen in the stack. | | headerBlurEffect | Blur effect for the translucent header. The headerTransparent option needs to be set to true for this to work. Supported values: extraLight, light, regular, prominent, systemUltraThinMaterial, systemThinMaterial, systemMaterial, systemThickMaterial, systemChromeMaterial, systemUltraThinMaterialLight, systemThinMaterialLight, systemMaterialLight, systemThickMaterialLight, systemChromeMaterialLight, dark, systemUltraThinMaterialDark, systemThinMaterialDark, systemMaterialDark, systemThickMaterialDark, systemChromeMaterialDark . Using both headerBlurEffect and scrollEdgeEffects (>= iOS 26) simultaneously may cause overlapping effects. | | headerLargeStyle | Style of the header when a large title is shown. The large title is shown if headerLargeTitleEnabled is true and the edge of any scrollable content reaches the matching edge of the header. Supported properties|

  • backgroundColor

| | headerLargeTitleEnabled | iOS | Whether to enable header with large title which collapses to regular header on scroll. Defaults to false. For large title to collapse on scroll, the content of the screen should be wrapped in a scrollable view such as ScrollView or FlatList. If the scrollable area doesn't fill the screen, the large title won't collapse on scroll. You also need to specify contentInsetAdjustmentBehavior="automatic" in your ScrollView, FlatList etc. | | headerLargeTitleShadowVisible | Android, iOS | Whether drop shadow of header is visible when a large title is shown. | | headerLargeTitleStyle | iOS | Style object for large title in header. Supported properties|

  • fontFamily
  • fontSize
  • fontWeight
  • color

| | headerLeft | Function which returns a React Element to display on the left side of the header. This replaces the back button. See headerBackVisible to show the back button along side left element. It receives the following properties in the arguments|

  • tintColor - The tint color to apply. Defaults to the theme's primary color.
  • canGoBack - Boolean indicating whether there is a screen to go back to.
  • label - Label text for the button. Usually the title of the previous screen.
  • href - The href to use for the anchor tag on web

| | headerRight | Function which returns a React Element to display on the right side of the header. It receives the following properties in the arguments|

  • tintColor - The tint color to apply. Defaults to the theme's primary color.
  • canGoBack - Boolean indicating whether there is a screen to go back to.

| | headerSearchBarOptions | Options to render a native search bar. Search bars are rarely static so normally it is controlled by passing an object to headerSearchBarOptions navigation option in the component's body. On iOS, you also need to specify contentInsetAdjustmentBehavior="automatic" in your ScrollView, FlatList etc. If you don't have a ScrollView, specify headerTransparent: false. Supported properties are: . ref . Ref to manipulate the search input imperatively. It contains the following methods|

  • focus - focuses the search bar
  • blur - removes focus from the search bar
  • setText - sets the search bar's content to given value
  • clearText - removes any text present in the search bar input field
  • cancelSearch - cancel the search and close the search bar
  • toggleCancelButton - depending on passed boolean value, hides or shows cancel button (only supported on iOS)

. autoCapitalize . Controls whether the text is automatically auto-capitalized as it is entered by the user. Possible values:

  • systemDefault
  • none
  • words
  • sentences
  • characters

. Defaults to systemDefault which is the same as sentences on iOS and none on Android. autoFocus . Whether to automatically focus search bar when it's shown. Defaults to false. barTintColor . The search field background color. By default bar tint color is translucent. tintColor . The color for the cursor caret and cancel button text. cancelButtonText . The text to be used instead of default Cancel button text. Deprecated starting from iOS 26. disableBackButtonOverride . Whether the back button should close search bar's text input or not. Defaults to false. hideNavigationBar . Boolean indicating whether to hide the navigation bar during searching. If left unset, system default is used. hideWhenScrolling . Boolean indicating whether to hide the search bar when scrolling. Defaults to true. inputType . The type of the input. Defaults to "text". Supported values: "text", "phone", "number", "email" . obscureBackground . Boolean indicating whether to obscure the underlying content with semi-transparent overlay. If left unset, system default is used. placement . Controls preferred placement of the search bar. Defaults to automatic. Supported values: automatic, stacked, inline, integrated, integratedButton, integratedCentered . allowToolbarIntegration . Boolean indicating whether the system can place the search bar among other toolbar items on iPhone. Set this prop to false to prevent the search bar from appearing in the toolbar when placement is automatic, integrated, integratedButton or integratedCentered. Defaults to true. If placement is set to stacked, the value of this prop will be overridden with false. Only supported on iOS, starting from iOS 26. placeholder . Text displayed when search field is empty. textColor . The color of the text in the search field. hintTextColor . The color of the hint text in the search field. headerIconColor . The color of the search and close icons shown in the header . shouldShowHintSearchIcon . Whether to show the search hint icon when search bar is focused. Defaults to true. onBlur . A callback that gets called when search bar has lost focus. onCancelButtonPress . A callback that gets called when the cancel button is pressed. onSearchButtonPress . A callback that gets called when the search button is pressed.

const [search, setSearch] = React.useState('');

React.useLayoutEffect(() => {
  navigation.setOptions({
    headerSearchBarOptions: {
      onSearchButtonPress: (event) => setSearch(event?.nativeEvent?.text),
    },
  });
}, [navigation]);

. onChangeText . A callback that gets called when the text changes. It receives the current text value of the search bar. headerShown . Whether to show the header. The header is shown by default. Setting this to false hides the header. | | headerShadowVisible | Whether to hide the elevation shadow (Android) or the bottom border (iOS) on the header. | | headerShown | Whether to show the header. The header is shown by default. Setting this to false hides the header. | | headerStyle | Style object for header. Supported properties|

  • backgroundColor

| | headerTintColor | Tint color for the header. Changes the color of back button and title. | | headerTitle | String or a function that returns a React Element to be used by the header. Defaults to title or name of the screen. When a function is passed, it receives tintColor andchildren in the options object as an argument. The title string is passed in children. Note that if you render a custom element by passing a function, animations for the title won't work. | | headerTitleAlign | How to align the header title. Possible values|

  • left
  • center

. Defaults to left on platforms other than iOS. Not supported on iOS. It's always center on iOS and cannot be changed. | | headerTitleStyle | Style object for header title. Supported properties|

  • fontFamily
  • fontSize
  • fontWeight
  • color

| | headerTransparent | Boolean indicating whether the navigation bar is translucent. Defaults to false. Setting this to true makes the header absolutely positioned - so that the header floats over the screen so that it overlaps the content underneath, and changes the background color to transparent unless specified in headerStyle. This is useful if you want to render a semi-transparent header or a blurred background. Note that if you don't want your content to appear under the header, you need to manually add a top margin to your content. React Navigation won't do it automatically. To get the height of the header, you can use HeaderHeightContext with React's Context API or useHeaderHeight. | | title | String that can be used as a fallback for headerTitle. | | unstable_headerInsets | Which edges of the native header apply window insets (e.g. statusbar inset) on Android. The native header applies insets to every edge by default. Setting an edge to false removes the inset for that edge: |

unstable_headerInsets: {
  top: false,
  bottom: false,
}

. Supported edges are top, left, right, and bottom. Disabling an inset also disables it for nested headers. A nested header cannot re-enable an inset disabled by a parent header. This API may change in a minor release. | | unstable_headerLeftItems | iOS | This option is experimental and may change in a minor release. Function which returns an array of items to display as on the left side of the header. This will override headerLeft if both are specified. It receives the following properties in the arguments|

  • tintColor - The tint color to apply. Defaults to the theme's primary color.
  • canGoBack - Boolean indicating whether there is a screen to go back to.

. See Header items for more information. | | unstable_headerRightItems | iOS | This option is experimental and may change in a minor release. Function which returns an array of items to display as on the right side of the header. This will override headerRight if both are specified. It receives the following properties in the arguments|

  • tintColor - The tint color to apply. Defaults to the theme's primary color.
  • canGoBack - Boolean indicating whether there is a screen to go back to.

. See Header items for more information. |

For additional details and navigator-specific examples, see React Navigation's Native Stack Navigator documentation.

Header buttons

You can add buttons to the header by using the headerLeft and headerRight options or <Stack.Toolbar> component. These options accept a React component that renders in the header.

Stack Toolbar — Configure iOS header toolbar with support for Liquid Glass.

Options API

import { Stack } from 'expo-router';
import { Button, Text, Image, StyleSheet } from 'react-native';
import { useState } from 'react';

function LogoTitle() {
  return (
    <Image style={styles.image} source={{ uri: 'https://reactnative.dev/img/tiny_logo.png' }} />
  );
}

export default function Home() {
  const [count, setCount] = useState(0);

  return (
    <>
      <Stack.Screen
        options={{
          headerTitle: props => <LogoTitle {...props} />,
          headerRight: () => <Button onPress={() => setCount(c => c + 1)} title="Update count" />,
        }}
      />
      <Text>Count: {count}</Text>
    </>
  );
}

const styles = StyleSheet.create({
  image: {
    width: 50,
    height: 50,
  },
});

Composition components

Screen composition API is in alpha and available in SDK 55 and later.

import { Stack } from 'expo-router';
import { Button, Text, Image, StyleSheet } from 'react-native';
import { useState } from 'react';

function LogoTitle() {
  return (
    <Image style={styles.image} source={{ uri: 'https://reactnative.dev/img/tiny_logo.png' }} />
  );
}

export default function Home() {
  const [count, setCount] = useState(0);

  return (
    <>
      <Stack.Title asChild>
        <LogoTitle />
      </Stack.Title>
      <Stack.Toolbar placement="right" asChild>
        <Button onPress={() => setCount(c => c + 1)} title="Update count" />
      </Stack.Toolbar>
      <Text>Count: {count}</Text>
    </>
  );
}

const styles = StyleSheet.create({
  image: {
    width: 50,
    height: 50,
  },
});

Other screen options

For a complete list of all available other screen options including animations, gestures, and other configurations:

Screen options

OptionPlatformDescription
animationAndroidHow the screen should animate when pushed or popped. Supported values: default, fade, fade_from_bottom, flip, simple_push, slide_from_bottom, slide_from_right, slide_from_left, none
animationDurationiOSChanges the duration (in milliseconds) of slide_from_bottom, fade_from_bottom, fade and simple_push transitions on iOS. Defaults to 350. For screens with default and flip transitions, and, as of now, for screens with presentation set to modal, formSheet, pageSheet (regardless of transition), the duration isn't customizable.
animationMatchesGestureiOSWhether the gesture to dismiss should use animation provided to animation prop. Defaults to false. Doesn't affect the behavior of screens presented modally.
animationTypeForReplaceAndroid, iOSThe type of animation to use when this screen replaces another screen. Defaults to push. Supported values: push, pop . This can be useful to provide appropriate animations, such as push for login and pop for logout.
autoHideHomeIndicatoriOSBoolean indicating whether the home indicator should prefer to stay hidden. Defaults to false.
contentStyleAndroid, iOSStyle object for the scene content.
freezeOnBluriOSBoolean indicating whether to prevent inactive screens from re-rendering. Defaults to false. Defaults to true when enableFreeze() from react-native-screens package is run at the top of the application. Only supported on iOS and Android.
fullScreenGestureEnablediOSWhether the gesture to dismiss should work on the whole screen. Using gesture to dismiss with this option results in the same transition animation as simple_push. This behavior can be changed by setting customAnimationOnGesture prop. Achieving the default iOS animation isn't possible due to platform limitations. Defaults to false. Doesn't affect the behavior of screens presented modally.
fullScreenGestureShadowEnabledAndroid, iOSWhether the full screen dismiss gesture has shadow under view during transition. Defaults to true. This does not affect the behavior of transitions that don't use gestures enabled by fullScreenGestureEnabled prop.
gestureDirectioniOSSets the direction in which you should swipe to dismiss the screen. Supported values: vertical, horizontal . When using vertical option, options fullScreenGestureEnabled: true, customAnimationOnGesture: true and animation: 'slide_from_bottom' are set by default.
gestureEnablediOSWhether you can use gestures to dismiss this screen. Defaults to true.
navigationBarColorAndroidThis option is deprecated and will be removed in a future release (for apps targeting Android SDK 35 or above edge-to-edge mode is enabled by default and it is expected that the edge-to-edge will be enforced in future SDKs, see here for more information). Sets the navigation bar color. Defaults to initial status bar color.
navigationBarHiddenAndroidBoolean indicating whether the navigation bar should be hidden. Defaults to false.
orientationAndroidThe display orientation to use for the screen. Supported values: default, all, portrait, portrait_up, portrait_down, landscape, landscape_left, landscape_right
presentationAndroidHow should the screen be presented. Supported values: card, modal, containedModal, fullScreenModal, transparentModal, containedTransparentModal, formSheet
scrollEdgeEffectsiOSConfigures the scroll edge effect for the content ScrollView (the ScrollView that is present in first descendants chain of the Screen). Depending on values set, it will blur the scrolling content below certain UI elements (e.g. header items, search bar) for the specified edge of the ScrollView. When set in nested containers, i.e. Native Stack inside Native Bottom Tabs, or the other way around, the ScrollView will use only the innermost one's config. Edge effects can be configured for each edge separately. The following values are currently supported
  • automatic - the automatic scroll edge effect style,
  • hard - a scroll edge effect with a hard cutoff and dividing line,
  • soft - a soft-edged scroll edge effect,
  • hidden - no scroll edge effect.

. Defaults to automatic for each edge. Using both blurEffect and scrollEdgeEffects (>= iOS 26) simultaneously may cause overlapping effects. Only supported on iOS, starting from iOS 26. | | sheetAllowedDetents | Describes heights where a sheet can rest. Supported values: 'fitToContents' or an array of fractions (e.g. [0.25, 0.5, 0.75]). The array must be sorted in ascending order. This invariant is verified only in development mode, where violation results in an error. Android is limited to 3 detents. Defaults to [1.0]. See Configuring sheet sizes for usage examples. | | sheetCornerRadius | The corner radius of the sheet. If set to a non-negative value it will use the provided radius, otherwise the system default is used. | | sheetElevation | Integer value describing elevation of the sheet, impacting shadow on the top edge. Not dynamic - changing it after the component is rendered won't have an effect. Defaults to 24. | | sheetExpandsWhenScrolledToEdge | Whether the sheet should expand to a larger detent when scrolling. The ScrollView must be reachable by following the first child view at each level of the view hierarchy from the screen component. Defaults to true. See Scroll behavior for more details. | | sheetGrabberVisible | Whether the sheet shows a grabber handle at the top. Defaults to false. | | sheetInitialDetentIndex | Index of the detent the sheet should expand to after being opened. If the specified index is out of bounds of the sheetAllowedDetents array, an error will be thrown in development mode and the value will be reset to the default in production. Can also be set to 'last' to open at the largest detent. Defaults to 0. See Configuring sheet sizes for usage examples. | | sheetLargestUndimmedDetentIndex | The largest detent index for which the view underneath won't be dimmed. Can be set to a number (index into sheetAllowedDetents), 'none' (always dim), or 'last' (never dim). Defaults to 'none'. See Controlling dimming for usage examples. | | sheetResizeAnimationEnabled | Whether the default native animation should be used when the sheet's content size changes (specifically when using fitToContents). Set to false to implement custom resizing animations. Defaults to true. | | sheetShouldOverflowTopInset | Whether the sheet content should be rendered behind the status bar or display cutouts. When true, detent ratios in sheetAllowedDetents are measured relative to the full stack height. When false, they are measured relative to the adjusted height (excluding the top inset). Defaults to false. | | statusBarAnimation | Sets the status bar animation (similar to the StatusBar component). Defaults to fade on iOS and none on Android. Supported values: "fade", "none", "slide" . On Android, setting either fade or slide will set the transition of status bar color. On iOS, this option applies to the appearance animation of the status bar. Requires setting View controller-based status bar appearance -> YES (or removing the config) in your Info.plist file. | | statusBarBackgroundColor | This option is deprecated and will be removed in a future release (for apps targeting Android SDK 35 or above edge-to-edge mode is enabled by default and it is expected that the edge-to-edge will be enforced in future SDKs, see here for more information). Sets the background color of the status bar (similar to the StatusBar component). | | statusBarHidden | Whether the status bar should be hidden on this screen. Requires setting View controller-based status bar appearance -> YES (or removing the config) in your Info.plist file. | | statusBarStyle | Sets the status bar color (similar to the StatusBar component). Supported values: "auto", "inverted", "dark", "light" . Defaults to auto on iOS and light on Android. Requires setting View controller-based status bar appearance -> YES (or removing the config) in your Info.plist file. | | statusBarTranslucent | This option is deprecated and will be removed in a future release (for apps targeting Android SDK 35 or above edge-to-edge mode is enabled by default and it is expected that the edge-to-edge will be enforced in future SDKs, see here for more information). Sets the translucency of the status bar (similar to the StatusBar component). Defaults to false. | | tabBarAccessibilityLabel | Accessibility label for the tab button. This is read by the screen reader when the user taps the tab. It's recommended to set this if you don't have a label for the tab. | | tabBarActiveBackgroundColor | Background color for the active tab. | | tabBarActiveTintColor | Color for the icon and label in the active tab. | | tabBarBackground | Function which returns a React Element to use as background for the tab bar. You could render an image, a gradient, blur view etc.: |

import { BlurView } from 'expo-blur';
import { StyleSheet } from 'react-native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

// ...

createBottomTabNavigator({
  screenOptions: {
    tabBarStyle: { position: 'absolute' },
    tabBarBackground: () => (
      <BlurView tint="light" intensity={100} style={StyleSheet.absoluteFill} />
    ),
  },
  screens: {
    // ...
  },
});

.

import { BlurView } from 'expo-blur';
import { StyleSheet } from 'react-native';

// ...

<Tab.Navigator
  screenOptions={{
    tabBarStyle: { position: 'absolute' },
    tabBarBackground: () => (
      <BlurView tint="light" intensity={100} style={StyleSheet.absoluteFill} />
    ),
  }}
>

. When using BlurView, make sure to set position: 'absolute' in tabBarStyle as well. You'd also need to use useBottomTabBarHeight to add bottom padding to your content. | | tabBarBadge | Text to show in a badge on the tab icon. Accepts a string or a number. | | tabBarBadgeStyle | Style for the badge on the tab icon. You can specify a background color or text color here. | | tabBarButton | Function which returns a React element to render as the tab bar button. It wraps the icon and label. Renders PlatformPressable by default. You can specify a custom implementation here: |

tabBarButton: (props) => <TouchableOpacity {...props} />;

| | tabBarButtonTestID | ID to locate this tab button in tests. | | tabBarHideOnKeyboard | Whether the tab bar is hidden when the keyboard opens. Defaults to false. | | tabBarIcon | Function that given { focused: boolean, color: string, size: number } returns a React.Node, to display in the tab bar. | | tabBarIconStyle | Style object for the tab icon. | | tabBarInactiveBackgroundColor | Background color for the inactive tabs. | | tabBarInactiveTintColor | Color for the icon and label in the inactive tabs. | | tabBarItemStyle | Style object for the tab item container. | | tabBarLabel | Title string of a tab displayed in the tab bar or a function that given { focused: boolean, color: string } returns a React.Node, to display in tab bar. When undefined, scene title is used. To hide, see tabBarShowLabel. | | tabBarLabelPosition | Whether the label is shown below the icon or beside the icon. By default, the position is chosen automatically based on device width|

  • . below-icon . The label is shown below the icon (typical for devices with smaller widths such as phones)
  • . beside-icon . The label is shown next to the icon (typical for larger devices such as tablets)

| | tabBarLabelStyle | Style object for the tab label. | | tabBarPosition | Position of the tab bar. Available values are|

  • bottom (Default)
  • top
  • left
  • right

. When the tab bar is positioned on the left or right, it is styled as a sidebar. This can be useful when you want to show a sidebar on larger screens and a bottom tab bar on smaller screens: . You can also render a compact sidebar by placing the label below the icon. This is only supported when the tabBarVariant is set to material: | | tabBarShowLabel | Whether the tab label should be visible. Defaults to true. | | tabBarStyle | Style object for the tab bar. You can configure styles such as background color here. To show your screen under the tab bar, you can set the position style to absolute: |

createBottomTabNavigator({
  screenOptions: {
    tabBarStyle: { position: 'absolute' },
  },
  screens: {
    // ...
  },
});

.

<Tab.Navigator
  screenOptions={{
    tabBarStyle: { position: 'absolute' },
  }}
>

. You also might need to add a bottom margin to your content if you have an absolutely positioned tab bar. React Navigation won't do it automatically. See useBottomTabBarHeight for more details. | | tabBarVariant | Variant of the tab bar. Available values are|

  • uikit (Default) - The tab bar will be styled according to the iOS UIKit guidelines.
  • material - The tab bar will be styled according to the Material Design guidelines.

. The material variant is currently only supported when the tabBarPosition is set to left or right. |

For additional details and navigator-specific examples, see React Navigation's Native Stack Navigator documentation.

Custom push behavior

By default, the Stack navigator removes duplicate screens when pushing a route that is already in the stack. For example, if you push the same screen twice, the second push will be ignored. You can change this push behavior by providing a custom getId() function to the <Stack.Screen>.

For example, the index route in the following layout structure shows a list of different user profiles in the app. Let's make the [details] route a dynamic route so that the app user can navigate to see a profile's details.

src

app

  _layout.tsx

  index.tsx

  [details].tsx``matches dynamic paths like '/details1'

The Stack navigator will push a new screen every time the app user navigates to a different profile but will fail. If you provide a getId() function that returns a new ID every time, the Stack will push a new screen every time the app user navigates to a profile.

You can use the <Stack.Screen name="[profile]" getId={}> component in the layout component route to modify the push behavior:

import { Stack } from 'expo-router';

export default function Layout() {
  return (
    <Stack>
      <Stack.Screen
        name="[profile]"
        getId={
          ({ params }) => String(Date.now())
        }
      />
    </Stack>
  );
}

Removing stack screens

There are different actions you can use to dismiss and remove one or many routes from a stack.

dismiss action

Dismisses the last screen in the closest stack. If the current screen is the only route in the stack, it will dismiss the entire stack.

You can optionally pass a positive number to dismiss up to that specified number of screens.

Dismiss is different from back as it targets the closest stack and not the current navigator. If you have nested navigators, calling dismiss will take you back multiple screens.

import { Button, View } from 'react-native';
import { useRouter } from 'expo-router';

export default function Settings() {
  const router = useRouter();

  const handleDismiss = (count: number) => {
    router.dismiss(count)
  };

  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Button title="Go to first screen" onPress={() => handleDismiss(3)} />
    </View>
  );
}

dismissTo action

dismissTo was added in Expo Router 4.0.8. It operates similarly to the navigation function in Expo Router v3.

Dismisses screens in the current <Stack /> until the specified Href is reached. If the Href is absent in the history, a push action will be performed instead.

For example, consider the history of /one, /two, /three routes, where /three is the current route. The action router.dismissTo('/one') will cause the history to go back twice, while router.dismissTo('/four') will push the history forward to the /four route.

import { Button, View, Text } from 'react-native';
import { useRouter } from 'expo-router';

export default function Settings() {
  const router = useRouter();

  const handleDismissAll = () => {
    router.dismissTo('/')
  };

  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Button title="Go to first screen" onPress={handleDismissAll} />
    </View>
  );
}

dismissAll action

To return to the first screen in the closest stack. This is similar to popToTop stack action.

For example, the home route is the first screen, and the settings is the last. To go from settings to home route you'll have to go back to details. However, using the dismissAll action, you can go from settings to home and dismiss any screen in between.

import { Button, View, Text } from 'react-native';
import { useRouter } from 'expo-router';

export default function Settings() {
  const router = useRouter();

  const handleDismissAll = () => {
    router.dismissAll()
  };

  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Button title="Go to first screen" onPress={handleDismissAll} />
    </View>
  );
}

canDismiss action

To check if it is possible to dismiss the current screen. Returns true if the router is within a stack with more than one screen in the stack's history.

import { Button, View } from 'react-native';
import { useRouter } from 'expo-router';

export default function Settings() {
  const router = useRouter();

  const handleDismiss = (count: number) => {
    if (router.canDismiss()) {
      router.dismiss(count)
    }
  };

  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Button title="Maybe dismiss" onPress={() => handleDismiss()} />
    </View>
  );
}

iOS 26 Liquid Glass headers

Starting from iOS 26, navigation headers adopt the system's "Liquid Glass" effect by default. It cannot be disabled per screen, so you need to opt out using a global configuration.

Method 1: Use UIDesignRequiresCompatibility

Note: Not supported in Expo Go.This method is a temporary workaround. From iOS 27, this option will be removed by Apple and you cannot opt out of the Liquid Glass effect.

Create a development build and set the UIDesignRequiresCompatibility property to true in app config:

{
  "ios": {
    "infoPlist": {
      "UIDesignRequiresCompatibility": true
    }
  }
}

Method 2: Use JavaScript-based navigation stack

Swap the native stack for the JavaScript-driven stack shipped at expo-router/js-stack. This gives you full control over the header UI, at the cost of the performance benefits of the highly optimized iOS navigation views and controllers:

import { Stack as JsStack } from 'expo-router/js-stack';

export default function Layout() {
  return <JsStack />;
}

The expo-router/js-stack entry point is the SDK 56 replacement for the @react-navigation/stack library. See the SDK 55 to 56 migration guide for more information.

Common problems

Large title does not collapse when scrolling

When using headerLargeTitle: true (or <Stack.Title large>) with a ScrollView or FlatList, the large title may not collapse on scroll. This happens when the scrollable view is not the direct first child of the screen component.

To fix this, ensure ScrollView or FlatList is the first child rendered by your screen component. If you need a wrapper, set collapsable={false} on it:

import { Stack } from 'expo-router';
import { ScrollView, View, Text } from 'react-native';

export default function Home() {
  return (
    <ScrollView>
      <Stack.Title large>Home</Stack.Title>
      <Text>Content here</Text>
    </ScrollView>
  );
}

If you need to wrap the ScrollView, set collapsable={false} on the wrapper:

import { Stack } from 'expo-router';
import { ScrollView, View, Text } from 'react-native';

export default function Home() {
  return (
    <View collapsable={false}>
      <ScrollView>
        <Stack.Title large>Home</Stack.Title>
        <Text>Content here</Text>
      </ScrollView>
    </View>
  );
}

White background flashes when navigating between screens

A white flash between screen transitions usually means the navigation stack is using a light background while your app uses a dark theme.

To fix this, wrap your root layout with Expo Router's <ThemeProvider> and pass the appropriate theme:

import { ThemeProvider, DarkTheme, DefaultTheme, Stack } from 'expo-router';
import { useColorScheme } from 'react-native';

export default function RootLayout() {
  const colorScheme = useColorScheme();

  return (
    <ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
      <Stack />
    </ThemeProvider>
  );
}

For apps that are always dark-themed:

import { ThemeProvider, DarkTheme, Stack } from 'expo-router';

export default function RootLayout() {
  return (
    <ThemeProvider value={DarkTheme}>
      <Stack />
    </ThemeProvider>
  );
}

API reference

An Expo Router API that provides Stack navigator, toolbar, and screen components.

See the Expo Router reference for installation and configuration.

Usage

import { Stack } from 'expo-router';

export default function Layout() {
  return <Stack />;
}

For more information about using stack navigator, read the stack layout guide:

Stack layout — Learn how to use the Stack layout in Expo Router.

API

import { Stack } from 'expo-router';

Components

Stack

Renders a native stack navigator.

Stack.Header

Type: React.Element<StackHeaderProps>

The component used to configure header styling for a stack screen.

Use this component to set header appearance properties like blur effect, background color, and shadow visibility.

Example

import { Stack } from 'expo-router';

export default function Page() {
  return (
    <>
      <Stack.Header
        blurEffect="systemMaterial"
        style={{ backgroundColor: '#fff' }}
      />
      <ScreenContent />
    </>
  );
}

Example

When used inside a layout with Stack.Screen:

import { Stack } from 'expo-router';

export default function Layout() {
  return (
    <Stack>
      <Stack.Screen name="index">
        <Stack.Header blurEffect="systemMaterial" />
      </Stack.Screen>
    </Stack>
  );
}

Note: If multiple instances of this component are rendered for the same screen, the last one rendered in the component tree takes precedence.

StackHeaderProps

asChild

Optional • Type: boolean • Default: false

When true, renders children as a custom header component, replacing the default header entirely. Use this to implement fully custom header layouts.

blurEffect

Optional • Type: BlurEffect

The blur effect to apply to the header background on iOS. Common values include 'regular', 'prominent', 'systemMaterial', etc.

children

Optional • Type: ReactNode

Child elements for custom header when asChild is true.

hidden

Optional • Type: boolean • Default: false

Whether to hide the header completely. When set to true, the header will not be rendered.

largeStyle

Optional • Type: StyleProp<{ backgroundColor: ColorValue, shadowColor: 'transparent' }>

Style properties for the large title header (iOS).

  • backgroundColor: Background color of the large title header
  • shadowColor: Set to 'transparent' to hide the large title shadow/border

style

Optional • Type: StyleProp<{ backgroundColor: ColorValue, color: ColorValue, shadowColor: 'transparent' }>

Style properties for the standard-sized header.

  • color: Tint color for header elements (similar to tintColor in React Navigation)
  • backgroundColor: Background color of the header
  • shadowColor: Set to 'transparent' to hide the header shadow/border

transparent

Optional • Type: boolean • Default: false

Whether the header should be transparent. When true, the header is absolutely positioned and content scrolls underneath.

Auto-enabled when:

  • style.backgroundColor is 'transparent'
  • blurEffect is set (required for blur to work)

Stack.Screen

Type: React.Element<StackScreenProps>

StackScreenProps

dangerouslySingular

Optional • Type: SingularOptions

When enabled, the navigator will reuse an existing screen instead of pushing a new one.

Only supported when used inside a Layout component.

Deprecated: Use dangerouslySingular instead.

Only supported when used inside a Layout component.

getId

Optional • Type: (__namedParameters: { params: Record<string, any> }) => string | undefined

Function to determine a unique ID for the screen.

initialParams

Optional • Type: Record<string, any>

Initial params to pass to the route.

Only supported when used inside a Layout component.

listeners

Optional • Literal type: union

Listeners for navigation events.

Only supported when used inside a Layout component.

Acceptable values are: Partial<{ beforeRemove: EventListenerCallback<NativeStackNavigationEventMap & EventMapCore<StackNavigationState<ParamListBase>>, 'beforeRemove', true>, blur: EventListenerCallback<NativeStackNavigationEventMap & EventMapCore<StackNavigationState<ParamListBase>>, 'blur', unknown>, focus: EventListenerCallback<NativeStackNavigationEventMap & EventMapCore<StackNavigationState<ParamListBase>>, 'focus', unknown>, gestureCancel: EventListenerCallback<NativeStackNavigationEventMap & EventMapCore<StackNavigationState<ParamListBase>>, 'gestureCancel', unknown>, sheetDetentChange: EventListenerCallback<NativeStackNavigationEventMap & EventMapCore<StackNavigationState<ParamListBase>>, 'sheetDetentChange', unknown>, state: EventListenerCallback<NativeStackNavigationEventMap & EventMapCore<StackNavigationState<ParamListBase>>, 'state', unknown>, transitionEnd: EventListenerCallback<NativeStackNavigationEventMap & EventMapCore<StackNavigationState<ParamListBase>>, 'transitionEnd', unknown>, transitionStart: EventListenerCallback<NativeStackNavigationEventMap & EventMapCore<StackNavigationState<ParamListBase>>, 'transitionStart', unknown> }> | (prop: { navigation: any, route: RouteProp<ParamListBase, string> }) => ScreenListeners<TState, TEventMap>

name

Optional • Type: string

Name is required when used inside a Layout component.

options

Optional • Literal type: union

Options to configure the screen.

Accepts an object or a function returning an object. The function form options={({ route }) => ({})} is only supported when used inside a Layout component. When used inside a page component, pass an options object directly.

Acceptable values are: NativeStackNavigationOptions | (prop: { navigation: any, route: RouteProp<ParamListBase, string> }) => NativeStackNavigationOptions

redirect

Optional • Type: boolean

Redirect to the nearest sibling route. If all children are redirect={true}, the layout will render null as there are no children to render.

Only supported when used inside a Layout component.

Inherited props
  • PropsWithChildren

Stack.SearchBar

Type: React.Element<StackSearchBarProps>

A search bar component that integrates with the native stack header.

Note: Using Stack.SearchBar will automatically make the header visible (headerShown: true), as the search bar is rendered as part of the native header.

To display the search bar in the bottom toolbar on iOS 26+, use Stack.Toolbar.SearchBarSlot inside Stack.Toolbar.

Example

import { Stack } from 'expo-router';

export default function Page() {
  return (
    <>
      <Stack.SearchBar
        placeholder="Search..."
        onChangeText={(event) => console.log(event.nativeEvent.text)}
      />
     <ScreenContent />
    </>
  );
}

StackSearchBarProps

Inherited props
  • SearchBarProps

Stack.Title

Type: React.Element<StackTitleProps>

Component to set the screen title.

Can be used inside Stack.Screen in a layout or directly inside a screen component.

Example

String title in a layout:

import { Stack } from 'expo-router';

export default function Layout() {
  return (
    <Stack>
      <Stack.Screen name="index">
        <Stack.Title large>Home</Stack.Title>
      </Stack.Screen>
    </Stack>
  );
}

Example

String title inside a screen:

import { Stack } from 'expo-router';

export default function Page() {
  return (
    <>
      <Stack.Title>My Page</Stack.Title>
      <ScreenContent />
    </>
  );
}

Example

Custom component as the title using asChild:

import { Stack } from 'expo-router';

export default function Layout() {
  return (
    <Stack>
      <Stack.Screen name="index">
        <Stack.Title asChild>
          <MyCustomTitle />
        </Stack.Title>
      </Stack.Screen>
    </Stack>
  );
}

Note: If multiple instances of this component are rendered for the same screen, the last one rendered in the component tree takes precedence.

StackTitleProps

asChild

Optional • Type: boolean

Use this to render a custom component as the header title.

Example

<Stack.Title asChild>
  <MyCustomTitle />
</Stack.Title>

children

Optional • Type: ReactNode

The title content. Pass a string for a plain text title, or a custom component when asChild is enabled.

large

Optional • Type: boolean

Enables large title mode.

largeStyle

Optional • Type: StyleProp<{ color: ColorValue, fontFamily: TextStyle[fontFamily], fontSize: TextStyle[fontSize], fontWeight: Exclude<TextStyle[fontWeight], number> }>

Style properties for the large title header.

style

Optional • Type: StyleProp<{ color: ColorValue, fontFamily: TextStyle[fontFamily], fontSize: TextStyle[fontSize], fontWeight: Exclude<TextStyle[fontWeight], number>, textAlign: 'left' | 'center' }>

Stack.Toolbar

Type: React.Element<StackToolbarProps>

The component used to configure the stack toolbar.

  • Use placement="left" to customize the left side of the header.
  • Use placement="right" to customize the right side of the header.
  • Use placement="bottom" (default) to show a bottom toolbar.

If multiple instances of this component are rendered for the same screen, the last one rendered in the component tree takes precedence.

Note: Using Stack.Toolbar with placement="left" or placement="right" will automatically make the header visible (headerShown: true), as the toolbar is rendered as part of the native header.

Note: Stack.Toolbar with placement="bottom" can only be used inside page components, not in layout components.

Example

import { Stack } from 'expo-router';

export default function Layout() {
  return (
    <Stack>
      <Stack.Screen name="index">
        <Stack.Toolbar placement="left">
          <Stack.Toolbar.Button icon="sidebar.left" onPress={() => alert('Left button pressed!')} />
        </Stack.Toolbar>
        <Stack.Toolbar placement="right">
          <Stack.Toolbar.Button icon="ellipsis.circle" onPress={() => alert('Right button pressed!')} />
        </Stack.Toolbar>
      </Stack.Screen>
    </Stack>
  );
}

Example

import { Stack } from 'expo-router';

export default function Page() {
  return (
    <>
      <Stack.Toolbar placement="left">
        <Stack.Toolbar.Button icon="sidebar.left" onPress={() => alert('Left button pressed!')} />
      </Stack.Toolbar>
      <Stack.Toolbar>
        <Stack.Toolbar.Spacer />
        <Stack.Toolbar.Button icon="magnifyingglass" onPress={() => {}} />
        <Stack.Toolbar.Spacer />
      </Stack.Toolbar>
      <ScreenContent />
    </>
  );
}

StackToolbarProps

asChild

Optional • Type: boolean • Default: false

When true, renders children as a custom component in the header area, replacing the default header layout.

Only applies to placement="left" and placement="right".

children

Optional • Type: ReactNode

Child elements to compose the toolbar. Can include Stack.Toolbar.Button, Stack.Toolbar.Menu, Stack.Toolbar.View, Stack.Toolbar.Spacer, and Stack.Toolbar.SearchBarSlot (bottom placement, iOS only) components.

placement

Optional • Type: ToolbarPlacement • Default: 'bottom'

The placement of the toolbar.

  • 'left': Renders items in the left area of the header.
  • 'right': Renders items in the right area of the header.
  • 'bottom': Renders items in the bottom toolbar.

Stack.Screen.BackButton

Type: React.Element<StackScreenBackButtonProps>

Component to configure the back button.

Can be used inside Stack.Screen in a layout or directly inside a screen component.

Example

import { Stack } from 'expo-router';

export default function Layout() {
  return (
    <Stack>
      <Stack.Screen name="detail">
        <Stack.Screen.BackButton displayMode="minimal">Back</Stack.Screen.BackButton>
      </Stack.Screen>
    </Stack>
  );
}

Example

import { Stack } from 'expo-router';

export default function Page() {
  return (
    <>
      <Stack.Screen.BackButton hidden />
      <ScreenContent />
    </>
  );
}

Note: If multiple instances of this component are rendered for the same screen, the last one rendered in the component tree takes precedence.

StackScreenBackButtonProps

children

Optional • Type: string

The title to display for the back button.

displayMode

Optional • Type: BackButtonDisplayMode

The display mode for the back button.

hidden

Optional • Type: boolean

Whether to hide the back button.

src

Optional • Type: ImageSourcePropType

Custom image source for the back button.

style

Optional • Type: StyleProp<{ fontFamily: string, fontSize: number }>

Style for the back button title.

withMenu

Optional • Type: boolean

Whether to show a context menu when long pressing the back button.

Stack.Toolbar.Badge

Type: React.Element<FC<StackToolbarBadgeProps>>

StackToolbarBadgeProps

children

Optional • Type: string

The text to display as the badge

style

Optional • Type: StyleProp<Pick<TextStyle, 'fontFamily' | 'fontWeight' | 'color' | 'backgroundColor' | 'fontSize'>>

Stack.Toolbar.Button

Type: React.Element<FC<StackToolbarButtonProps>>

StackToolbarButtonProps

accessibilityHint

Optional • Type: string

accessibilityLabel

Optional • Type: string

Accessibility label spoken by screen readers (TalkBack/VoiceOver).

children

Optional • Type: ReactNode

There are two ways to specify the content of the button:

Example

import { Stack } from 'expo-router';

export default function Page() {
  return (
    <>
      <Stack.Toolbar placement="left">
        <Stack.Toolbar.Button icon="star.fill">As text passed as children</Stack.Toolbar.Button>
      </Stack.Toolbar>
      <ScreenContent />
    </>
  );
}

Example

import { Stack } from 'expo-router';

export default function Page() {
  return (
    <>
      <Stack.Toolbar placement="left">
        <Stack.Toolbar.Button>
          <Stack.Toolbar.Icon sf="star.fill" />
          <Stack.Toolbar.Label>As components</Stack.Toolbar.Label>
          <Stack.Toolbar.Badge>3</Stack.Toolbar.Badge>
        </Stack.Toolbar.Button>
      </Stack.Toolbar>
      <ScreenContent />
    </>
  );
}

Note: When icon is used, the label will not be shown and will be used for accessibility purposes only. Badge is only supported in left/right placements, not in bottom (iOS toolbar limitation).

disabled

Optional • Type: boolean

hidden

Optional • Type: boolean • Default: false

Whether the button should be hidden.

hidesSharedBackground

iOS 26+

Optional • Type: boolean

Whether to hide the shared background.

icon

Optional • Literal type: union

Icon to display in the button.

On iOS, it can be a string representing an SFSymbol, an image source or xcasset.

Note: When used in placement="bottom" on iOS, only string SFSymbols are supported. Use the image prop to provide custom images.

Acceptable values are: ImageSourcePropType | SFSymbols7_0

iconRenderingMode

Optional • Literal type: string

Controls how image-based icons are rendered.

  • 'template': applies tint color to the icon
  • 'original': preserves original icon colors (useful for multi-color icons)

Default behavior on iOS:

  • If tintColor is specified, defaults to 'template'
  • If no tintColor, defaults to 'original'

On Android: defaults to 'template'.

This prop only affects image-based icons (not SF Symbols).

See: Apple documentation for more information.

Acceptable values are: 'template' | 'original'

image

Optional • Literal type: union

Image to display in the button.

Note: This prop is only supported in toolbar with placement="bottom".

Acceptable values are: SharedRef<'image', Record<never, never>> | null

onPress

Optional • Type: () => void

selected

Optional • Type: boolean

Whether the button is in a selected state

See: Apple documentation for more information

separateBackground

Optional • Type: boolean • Default: false

Whether to separate the background of this item from other header items.

style

Optional • Type: StyleProp<TextStyle>

Style for the label of the header item.

tintColor

Optional • Type: ColorValue

The tint color to apply to the button item.

See: - Apple documentation for more information.

variant

Optional • Literal type: string • Default: 'plain'

Acceptable values are: 'done' | 'plain' | 'prominent'

Stack.Toolbar.Icon

Type: React.Element<FC<StackToolbarIconProps>>

StackToolbarIconProps

renderingMode

Optional • Literal type: string

Controls how the image icon is rendered.

  • 'template': applies tint color to the icon
  • 'original': preserves original icon colors

Default behavior on iOS:

  • With parent tintColor: defaults to 'template'
  • Without parent tintColor: defaults to 'original'

On Android: defaults to 'template'. Setting 'original' skips the tint so the icon's source colors are preserved.

Acceptable values are: 'template' | 'original'

src

Type: ImageSourcePropType

sf

Type: SFSymbol

Name of an SF Symbol to display.

xcasset

Type: string

Name of an image in your Xcode asset catalog (.xcassets).

Stack.Toolbar.Label

Type: React.Element<FC<StackToolbarLabelProps>>

StackToolbarLabelProps

children

Optional • Type: string

The text to display as the label for the tab.

Stack.Toolbar.Menu

Type: React.Element<FC<StackToolbarMenuProps>>

StackToolbarMenuProps

accessibilityHint

Optional • Type: string

accessibilityLabel

Optional • Type: string

Accessibility label spoken by screen readers (TalkBack/VoiceOver).

children

Optional • Type: ReactNode

Menu content - can include icons, labels, badges and menu actions.

Example

<Stack.Toolbar.Menu>
  <Stack.Toolbar.Icon sfSymbol="ellipsis.circle" />
  <Stack.Toolbar.Label>Options</Stack.Toolbar.Label>
  <Stack.Toolbar.MenuAction onPress={() => {}}>Action 1</Stack.Toolbar.MenuAction>
</Stack.Toolbar.Menu>

destructive

Optional • Type: boolean

If true, the menu item will be displayed as destructive.

See: Apple documentation for more information.

disabled

Optional • Type: boolean

elementSize

iOS 16+

Optional • Literal type: string

The preferred size of the menu elements.

Note: This prop is only supported in Stack.Toolbar.Bottom.

See: Apple documentation for more information.

Acceptable values are: 'small' | 'medium' | 'auto' | 'large'

hidden

Optional • Type: boolean • Default: false

Whether the menu should be hidden.

hidesSharedBackground

iOS 26+

Optional • Type: boolean

Whether to hide the shared background.

See: Official Apple documentation for more information.

icon

Optional • Literal type: union

Icon for the menu item.

Can be an SF Symbol name or an image source.

Note: When used in placement="bottom" on iOS, only string SFSymbols are supported. Use the image prop to provide custom images.

Acceptable values are: ImageSourcePropType | SFSymbols7_0

iconRenderingMode

Optional • Literal type: string

Controls how image-based icons are rendered.

  • 'template': applies tint color to the icon (useful for monochrome icons)
  • 'original': preserves original icon colors (useful for multi-color icons)

Default behavior on iOS:

  • If tintColor is specified, defaults to 'template'
  • If no tintColor, defaults to 'original'

On Android: defaults to 'template'.

This prop only affects image-based icons (not SF Symbols).

See: Apple documentation for more information.

Acceptable values are: 'template' | 'original'

image

Optional • Literal type: union

Image to display for the menu item.

Note: This prop is only supported in toolbar with placement="bottom".

Acceptable values are: SharedRef<'image', Record<never, never>> | null

inline

Optional • Type: boolean

If true, the menu will be displayed inline. This means that the menu will not be collapsed.

Note: Inline menus are only supported in submenus.

See: Apple documentation for more information.

palette

Optional • Type: boolean

If true, the menu will be displayed as a palette. This means that the menu will be displayed as one row.

Note: Palette menus are only supported in submenus.

See: Apple documentation for more information.

separateBackground

Optional • Type: boolean • Default: false

Whether to separate the background of this item from other header items.

style

Optional • Type: StyleProp<BasicTextStyle>

Style for the label of the header item.

tintColor

Optional • Type: ColorValue

The tint color to apply to the button item.

See: - Apple documentation for more information.

title

Optional • Type: string

Optional title to show on top of the menu.

variant

Optional • Literal type: string • Default: 'plain'

Acceptable values are: 'done' | 'plain' | 'prominent'

Stack.Toolbar.MenuAction

Type: React.Element<FC<StackToolbarMenuActionProps>>

StackToolbarMenuActionProps

children

Optional • Type: ReactNode

Can be an Icon, Label or string title.

destructive

Optional • Type: boolean

If true, the menu item will be displayed as destructive.

See: Apple documentation for more information.

disabled

Optional • Type: boolean

If true, the menu item will be disabled and not selectable.

See: Apple documentation for more information.

discoverabilityLabel

Optional • Type: string

An elaborated title that explains the purpose of the action.

hidden

Optional • Type: boolean

icon

Optional • Literal type: union

Icon for the menu action.

Can be an SF Symbol name or an image source.

Acceptable values are: ImageSourcePropType | SFSymbols7_0

iconRenderingMode

Optional • Literal type: string

Controls how image-based icons are rendered on iOS.

  • 'template': iOS applies tint color to the icon (useful for monochrome icons)
  • 'original': Preserves original icon colors (useful for multi-color icons)

Default behavior:

  • If tintColor is specified, defaults to 'template'
  • If no tintColor, defaults to 'original'

This prop only affects image-based icons (not SF Symbols).

See: Apple documentation for more information.

Acceptable values are: 'template' | 'original'

image

Optional • Literal type: union

Image to display for the menu action.

Note: This prop is only supported in Stack.Toolbar.Bottom.

Acceptable values are: SharedRef<'image', Record<never, never>> | null

isOn

Optional • Type: boolean

If true, the menu item will be displayed as selected.

onPress

Optional • Type: () => void

subtitle

Optional • Type: string

An optional subtitle for the menu item.

See: Apple documentation for more information.

unstable_keepPresented

Optional • Type: boolean

If true, the menu will be kept presented after the action is selected.

This is marked as unstable, because when action is selected on iOS it will recreate the menu, which will close all opened submenus and reset the scroll position.

See: Apple documentation for more information.

Stack.Toolbar.SearchBarSlot

Type: React.Element<FC<StackToolbarSearchBarSlotProps>>

StackToolbarSearchBarSlotProps

hidden

Optional • Type: boolean • Default: false

Whether the search bar slot should be hidden.

hidesSharedBackground

iOS 26+

Optional • Type: boolean

Whether to hide the shared background.

separateBackground

iOS 26+

Optional • Type: boolean

Whether this search bar slot has a separate background from adjacent items. When this prop is true, the search bar will always render as integratedButton.

In order to render the search bar with a separate background, ensure that adjacent toolbar items have separateBackground set to true or use Stack.Toolbar.Spacer to create spacing.

Example

<Stack.SearchBar onChangeText={()=>{}} />
<Stack.Toolbar placement="bottom">
  <Stack.Toolbar.SearchBarSlot />
  <Stack.Toolbar.Spacer />
  <Stack.Toolbar.Button icon="square.and.pencil" />
</Stack.Toolbar>

Stack.Toolbar.Spacer

Type: React.Element<FC<StackToolbarSpacerProps>>

StackToolbarSpacerProps

hidden

Optional • Type: boolean • Default: false

Whether the spacer should be hidden.

sharesBackground

iOS 26+

Optional • Type: boolean

Whether this spacer shares background with adjacent items.

Only available in bottom placement.

width

Optional • Type: number

The width of the spacing element.

In Left/Right placements, width is required. In Bottom placement, if width is not provided, the spacer will be flexible and expand to fill available space.

Stack.Toolbar.View

Type: React.Element<FC<StackToolbarViewProps>>

StackToolbarViewProps

asChild

Optional • Type: boolean • Default: false

When true, renders children as a custom component in the header area, replacing the default header layout.

Only applies to placement="left" and placement="right".

children

Optional • Type: ReactNode

Child elements to compose the toolbar. Can include Stack.Toolbar.Button, Stack.Toolbar.Menu, Stack.Toolbar.View, Stack.Toolbar.Spacer, and Stack.Toolbar.SearchBarSlot (bottom placement, iOS only) components.

placement

Optional • Type: ToolbarPlacement • Default: 'bottom'

The placement of the toolbar.

  • 'left': Renders items in the left area of the header.
  • 'right': Renders items in the right area of the header.
  • 'bottom': Renders items in the bottom toolbar.

children

Optional • Type: ReactElement<unknown, string | JSXElementConstructor<any>>

Can be any React node.

hidden

Optional • Type: boolean • Default: false

Whether the view should be hidden.

hidesSharedBackground

iOS 26+

Optional • Type: boolean

Whether to hide the shared background.

See: Official Apple documentation for more information.

separateBackground

Optional • Type: boolean • Default: false

Whether to separate the background of this item from other items.

Only available in bottom placement.

Interfaces

StackHeaderItemSharedProps

PropertyTypeDescription
accessibilityHint(optional)string-
accessibilityLabel(optional)string-
children(optional)ReactNode-
disabled(optional)boolean-
hidesSharedBackground(optional)boolean-
icon(optional)ImageSourcePropType | SFSymbols7_0-
iconRenderingMode(optional)'template' | 'original'Controls how image-based icons are rendered
  • 'template': applies tint color to the icon
  • 'original': preserves original icon colors (useful for multi-color icons)

. Default behavior on iOS:

  • If tintColor is specified, defaults to 'template'
  • If no tintColor, defaults to 'original'

. On Android: defaults to 'template'. The icon is always rendered through Compose's Icon and tinted unless 'original' is set explicitly. This prop only affects image-based icons (not SF Symbols). See: Apple documentation for more information. | | separateBackground(optional) | boolean | - | | style(optional) | StyleProp<BasicTextStyle> | - | | tintColor(optional) | ColorValue | - | | variant(optional) | 'done' \| 'plain' \| 'prominent' | Default: 'plain' |

Sources

The original Expo docs for this page:

  • Stack (source-of-truth/router/advanced/stack.md)
  • Router Stack (source-of-truth/versions/v57.0.0/sdk/router/stack.md)

On this page

Get startedScreen options and header configurationStatically configure route optionsConfigure header barSet screen options dynamicallyOptions APIComposition componentsAvailable header optionsHeader optionsHeader buttonsOptions APIComposition componentsOther screen optionsScreen optionsCustom push behaviorRemoving stack screensdismiss actiondismissTo actiondismissAll actioncanDismiss actioniOS 26 Liquid Glass headersMethod 1: Use UIDesignRequiresCompatibilityMethod 2: Use JavaScript-based navigation stackCommon problemsLarge title does not collapse when scrollingWhite background flashes when navigating between screensAPI referenceUsageAPIComponentsStackStack.HeaderasChildblurEffectchildrenhiddenlargeStylestyletransparentStack.ScreendangerouslySingulargetIdinitialParamslistenersnameoptionsredirectInherited propsStack.SearchBarInherited propsStack.TitleasChildchildrenlargelargeStylestyleStack.ToolbarasChildchildrenplacementStack.Screen.BackButtonchildrendisplayModehiddensrcstylewithMenuStack.Toolbar.BadgechildrenstyleStack.Toolbar.ButtonaccessibilityHintaccessibilityLabelchildrendisabledhiddenhidesSharedBackgroundiconiconRenderingModeimageonPressselectedseparateBackgroundstyletintColorvariantStack.Toolbar.IconrenderingModesrcsfxcassetStack.Toolbar.LabelchildrenStack.Toolbar.MenuaccessibilityHintaccessibilityLabelchildrendestructivedisabledelementSizehiddenhidesSharedBackgroundiconiconRenderingModeimageinlinepaletteseparateBackgroundstyletintColortitlevariantStack.Toolbar.MenuActionchildrendestructivedisableddiscoverabilityLabelhiddeniconiconRenderingModeimageisOnonPresssubtitleunstable_keepPresentedStack.Toolbar.SearchBarSlothiddenhidesSharedBackgroundseparateBackgroundStack.Toolbar.SpacerhiddensharesBackgroundwidthStack.Toolbar.ViewasChildchildrenplacementchildrenhiddenhidesSharedBackgroundseparateBackgroundInterfacesStackHeaderItemSharedPropsSources