xref: /expo/docs/pages/router/advanced/stack.mdx (revision 4db31584)
1---
2title: Stack
3description: Learn how to use the Stack Layout in Expo Router.
4---
5
6import { FileTree } from '~/ui/components/FileTree';
7import { BoxLink } from '~/ui/components/BoxLink';
8import { BookOpen02Icon } from '@expo/styleguide-icons';
9
10The `Stack` Layout in Expo Router wraps the [Native Stack Navigator](https://reactnavigation.org/docs/native-stack-navigator) from React Navigation, not to be confused with the legacy [JS Stack Navigator](https://reactnavigation.org/docs/stack-navigator).
11
12<FileTree files={['app/_layout.js', 'app/index.js', 'app/detail.js']} />
13
14To create a `Stack` layout with two screens as shown in the file structure above:
15
16```js app/_layout.js
17import { Stack } from 'expo-router/stack';
18
19export default function Layout() {
20  return <Stack />;
21}
22```
23
24## Configure header bar
25
26Use the `screenOptions` prop to configure the header bar.
27
28```jsx app/_layout.js
29import { Stack } from 'expo-router';
30
31export default function Layout() {
32  return (
33    <Stack
34      screenOptions={{
35        headerStyle: {
36          backgroundColor: '#f4511e',
37        },
38        headerTintColor: '#fff',
39        headerTitleStyle: {
40          fontWeight: 'bold',
41        },
42      }}
43    />
44  );
45}
46```
47
48You 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.
49
50```jsx app/home.js
51import { Link, Stack } from 'expo-router';
52import { Image, Text, View } from 'react-native';
53
54function LogoTitle() {
55  return (
56    <Image
57      style={{ width: 50, height: 50 }}
58      source={{ uri: 'https://reactnative.dev/img/tiny_logo.png' }}
59    />
60  );
61}
62
63export default function Home() {
64  return (
65    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
66      <Stack.Screen
67        options={{
68          // https://reactnavigation.org/docs/headers#setting-the-header-title
69          title: 'My home',
70          // https://reactnavigation.org/docs/headers#adjusting-header-styles
71          headerStyle: { backgroundColor: '#f4511e' },
72          headerTintColor: '#fff',
73          headerTitleStyle: {
74            fontWeight: 'bold',
75          },
76          // https://reactnavigation.org/docs/headers#replacing-the-title-with-a-custom-component
77          headerTitle: props => <LogoTitle {...props} />,
78        }}
79      />
80      <Text>Home Screen</Text>
81      <Link href={{ pathname: 'details', params: { name: 'Bacon' } }}>Go to Details</Link>
82    </View>
83  );
84}
85```
86
87You can use the imperative API `router.setParams()` function to configure the route dynamically.
88
89```jsx app/details.js
90import { View, Text } from 'react-native';
91import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
92
93export default function Details() {
94  const router = useRouter();
95  const params = useLocalSearchParams();
96
97  return (
98    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
99      <Stack.Screen
100        options={{
101          title: params.name,
102        }}
103      />
104      <Text
105        onPress={() => {
106          router.setParams({ name: 'Updated' });
107        }}>
108        Update the title
109      </Text>
110    </View>
111  );
112}
113```
114
115## Header buttons
116
117With the following file structure:
118
119<FileTree files={['app/_layout.js', 'app/home.js']} />
120
121You 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.
122
123```jsx app/_layout.js
124import { Stack } from 'expo-router';
125
126export default function Layout() {
127  return (
128    <Stack
129      // https://reactnavigation.org/docs/headers#sharing-common-options-across-screens
130      /* @info */
131      screenOptions={{
132        headerStyle: {
133          backgroundColor: '#f4511e',
134        },
135        headerTintColor: '#fff',
136        headerTitleStyle: {
137          fontWeight: 'bold',
138        },
139      }}>
140      /* @end */
141      {/* Optionally configure static options outside the route. */}
142      /* @info */
143      <Stack.Screen name="home" options={{}} />
144      /* @end */
145    </Stack>
146  );
147}
148```
149
150Use the `<Stack.Screen />` component in the child route to dynamically configure options.
151
152```jsx app/home.js
153import { Button, Text, Image } from 'react-native';
154/* @info */
155import { Stack } from 'expo-router';
156/* @end */
157
158function LogoTitle() {
159  return (
160    <Image
161      style={{ width: 50, height: 50 }}
162      source={require('@expo/snack-static/react-native-logo.png')}
163    />
164  );
165}
166
167export default function Home() {
168  const [count, setCount] = React.useState(0);
169
170  return (
171    <>
172      /* @info */
173      <Stack.Screen
174        options={{
175          headerTitle: props => <LogoTitle {...props} />,
176          headerRight: () => <Button onPress={() => setCount(c => c + 1)} title="Update count" />,
177        }}
178      />
179      /* @end */
180      <Text>Count: {count}</Text>
181    </>
182  );
183}
184```
185
186## Custom push behavior
187
188By 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`.
189
190<FileTree files={['app/_layout.js', 'app/[user].js']} />
191
192You 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.
193
194```jsx app/_layout.js
195import { Stack } from 'expo-router';
196
197export default function Layout() {
198  return (
199    <Stack>
200      <Stack.Screen
201        name="[user]"
202        /* @info Returning a new ID everytime will cause every page to push. */
203        getId={({ params }) => String(Date.now())}
204        /* @end */
205      />
206    </Stack>
207  );
208}
209```
210
211## Next steps
212
213<BoxLink
214  title="Native Stack Navigator: Options"
215  Icon={BookOpen02Icon}
216  description="For a list of all options, see React Navigation's documentation."
217  href="https://reactnavigation.org/docs/native-stack-navigator#options"
218/>
219
220<BoxLink
221  title="Tabs layout"
222  Icon={BookOpen02Icon}
223  description="Learn how to use Tabs layout in Expo Router."
224  href="/router/advanced/tabs"
225/>
226