Learn how to use the Stack Layout in Expo Router.
The Stack
Layout in Expo Router wraps the Native Stack Navigator from React Navigation, not to be confused with the legacy JS Stack Navigator.
app
_layout.js
index.js
detail.js
To create a Stack
layout with two screens as shown in the file structure above:
import { Stack } from 'expo-router/stack';
export default function Layout() {
return <Stack />;
}
Use the screenOptions
prop to configure the header bar.
import { Stack } from 'expo-router';
export default function Layout() {
return (
<Stack
screenOptions={{
headerStyle: {
backgroundColor: '#f4511e',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}
/>
);
}
You can use a layout's Screen component to configure the header bar dynamically from within the route. This is good for interactions that change the UI.
import { Link, Stack } from 'expo-router';
import { Image, Text, View } from 'react-native';
function LogoTitle() {
return (
<Image
style={{ width: 50, height: 50 }}
source={{ uri: 'https://reactnative.dev/img/tiny_logo.png' }}
/>
);
}
export default function Home() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Stack.Screen
options={{
// https://reactnavigation.org/docs/headers#setting-the-header-title
title: 'My home',
// https://reactnavigation.org/docs/headers#adjusting-header-styles
headerStyle: { backgroundColor: '#f4511e' },
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
// https://reactnavigation.org/docs/headers#replacing-the-title-with-a-custom-component
headerTitle: props => <LogoTitle {...props} />,
}}
/>
<Text>Home Screen</Text>
<Link href={{ pathname: 'details', params: { name: 'Bacon' } }}>Go to Details</Link>
</View>
);
}
You can use the imperative API router.setParams()
function to configure the route dynamically.
import { View, Text } from 'react-native';
import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
export default function Details() {
const router = useRouter();
const params = useLocalSearchParams();
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Stack.Screen
options={{
title: params.name,
}}
/>
<Text
onPress={() => {
router.setParams({ name: 'Updated' });
}}>
Update the title
</Text>
</View>
);
}
With the following file structure:
app
_layout.js
home.js
You can use the <Stack.Screen name={routeName} />
component in the layout component route to statically configure screen options. This is useful for tab bars or drawers which need to have an icon defined ahead of time.
import { Stack } from 'expo-router';
export default function Layout() {
return (
<Stack
// 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>
);
}
Use the <Stack.Screen />
component in the child route to dynamically configure options.
import { Button, Text, Image } from 'react-native';
import { Stack } from 'expo-router';
function LogoTitle() {
return (
<Image
style={{ width: 50, height: 50 }}
source={require('@expo/snack-static/react-native-logo.png')}
/>
);
}
export default function Home() {
const [count, setCount] = React.useState(0);
return (
<>
<Stack.Screen
options={{
headerTitle: props => <LogoTitle {...props} />,
headerRight: () => <Button onPress={() => setCount(c => c + 1)} title="Update count" />,
}}
/>
<Text>Count: {count}</Text>
</>
);
}
By default, the Stack
will deduplicate pages when pushing a route that is already in the stack. For example, if you push the same profile twice, the second push will be ignored. You can change the pushing behavior by providing a custom getId
function to the Stack.Screen
.
app
_layout.js
[user].js
You can use the <Stack.Screen name="[user]" getId={} />
component in the layout component route to modify the pushing behavior. In the following example, the getId
function pushes a new page every time the user navigates to a profile.
import { Stack } from 'expo-router';
export default function Layout() {
return (
<Stack>
<Stack.Screen
name="[user]"
getId={({ params }) => String(Date.now())}
/>
</Stack>
);
}
For a list of all options, see React Navigation's documentation.
Learn how to use Tabs layout in Expo Router.