Skip to main content

Nested navigators

The last navigation topic we'll look at in this intro course is nesting navigators.

You can nest navigators pretty much infinitely: a stack within a stack, a stack within a tab, a tab within a stack within a screen - anything goes really.

Converting a screen into a stack

To convert a screen into a stack of screens, the process is:

  1. create a folder with the same name as the screen and move the screen inside it
  2. rename the screen from its original name to index
  3. add a _layout.tsx file in the new folder, defining a stack with the single screen
  4. now you can add as many screens as you'd like!

Converting the index screen into a stack

Converting a file called index into a stack is slightly different in that you use a (grouping) route instead of making a folder called index:

  1. create a grouping folder (group) (the name can be anything here, it's not included in the route) and move the index file inside it
  2. the index filename stays the same

Steps 3 and 4 are the same as for named screens.

Convert the counter screen into a stack

Let's add a history screen to the counter screen.

First, create a folder with the same name as the screen and move the screen inside it.

Then, rename the screen from its original name to index

Note that a common side effect of stacking navigator is that every navigator comes with a header by default so you end up with many headers. To get around this, hide the header you don't need. In our case, let's hide the header in the root index file and ensure the new header has the correct title:

New file: app/counter/_layout.tsx
import { Stack } from "expo-router";

export default function Layout() {
return (
<Stack>
<Stack.Screen name="index" options={{ title: "Counter" }} />
</Stack>
);
}

Update: app/_layout.tsx
@@ -20,6 +20,7 @@ export default function Layout() {
name="counter"
options={{
title: "Counter",
+ headerShown: false,
tabBarIcon: ({ color, size }) => (
<AntDesign name="clockcircleo" size={size} color={color} />
),

Add the history screen

Create a new file for the history screen:

New file: app/counter/history.tsx
import { Text, View, StyleSheet } from "react-native";

export default function HistoryScreen() {
return (
<View style={styles.container}>
<Text style={styles.text}>History</Text>
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
text: {
fontSize: 24,
},
});

And add it to the layout route:

Update: app/counter/_layout.tsx
@@ -4,6 +4,7 @@ export default function Layout() {
return (
<Stack>
<Stack.Screen name="index" options={{ title: "Counter" }} />
+ <Stack.Screen name="history" options={{ title: "History" }} />
</Stack>
);
}

We could use Link or useRouter to navigate, but let's try something new and instead use an icon button in the header instead. Choose another icon from expo icons and render an icon button for the couter screen's headerRight.

Update: app/counter/_layout.tsx
@@ -1,9 +1,31 @@
-import { Stack } from "expo-router";
+import { Link, Stack } from "expo-router";
+import MaterialIcons from "@expo/vector-icons/MaterialIcons";
+
+import { theme } from "../../theme";
+import { Pressable } from "react-native";

export default function Layout() {
return (
<Stack>
- <Stack.Screen name="index" options={{ title: "Counter" }} />
+ <Stack.Screen
+ name="index"
+ options={{
+ title: "Counter",
+ headerRight: () => {
+ return (
+ <Link href="/counter/history" asChild>
+ <Pressable hitSlop={20}>
+ <MaterialIcons
+ name="history"
+ size={32}
+ color={theme.colorGrey}
+ />
+ </Pressable>
+ </Link>
+ );
+ },
+ }}
+ />
<Stack.Screen name="history" options={{ title: "History" }} />
</Stack>
);

Checkpoint

AndroidiOS