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:
- create a folder with the same name as the screen and move the screen inside it
- rename the screen from its original name to index
- add a _layout.tsx file in the new folder, defining a stack with the single screen
- 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:
- 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
- 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>
);
}
Navigate to the history screen with a header button
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
Android | iOS |
---|---|