Skip to main content

Layout groups

This app is going to have an onboarding step. The user will be faced with a special screen when they open the app for the first time. We're not doing auth in this workshop, but you could use a similar mechanism for setting up authenticated routes.

If we defined a new screen in the app directory, it would simply get rendered as one of the bottom tabs, so we need to group the bottom tabs out of the way.

For this we can use groups. Folders with () are grouping folders and they don't show up as part of the route.

Move the bottom tabs in a (tabs) group

Create a new folder called (tabs) and move both screens and the layout file inside.

Add a root layout file

Now create a new app/_layout.tsx file and render the (tabs) route as a stack. Because every screen gets rendered with a header by default, we'll need to set headerShown to false to avoid having duplicate headers for our tab navigator.

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

export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
</Stack>
);
}

Add the onboarding screen

Now let's create a new screen for the onboarding modal:

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

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

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

And the modal to the root layout:

Update app/_layout.tsx
@@ -4,7 +4,13 @@ export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
+ <Stack.Screen
+ name="onboarding"
+ options={{
+ presentation: "modal",
+ headerShown: false,
+ }}
+ />
</Stack>
);
}

Redirect

Finally, we'll use use Expo Router's Redirect component to redirect to the onboarding modal from the (tabs)/_layout.tsx:

Update (tabs)/_layout.tsx
@@ -1,9 +1,15 @@
-import { Tabs } from "expo-router";
+import { Redirect, Tabs } from "expo-router";
import Entypo from "@expo/vector-icons/Entypo";
import Feather from "@expo/vector-icons/Feather";
import { theme } from "@/theme";

+const hasFinishedOnboarding = false;
+
export default function Layout() {
+ if (!hasFinishedOnboarding) {
+ return <Redirect href="/onboarding" />;
+ }
+
return (
<Tabs screenOptions={{ tabBarActiveTintColor: theme.colorGreen }}>
<Tabs.Screen