Expo Router Drawer Navigation - From the Docs

Aaron K Saunders - Jul 26 '23 - - Dev Community

Overview

Welcome to the third installment of my Expo Router file-based routing video series! In this series, I will be diving into various aspects of Expo Router, including basic routing, dynamic routes, and the Drawer Navigator.

As I delved into working with the new router, I initially started with the basic two-tab template, which has been a valuable starting point. However, there were instances where I found myself puzzled by the magic happening behind the scenes.

To address this, I decided to take a step back and create these videos from scratch. Together, we will add Expo Router to a basic Expo application and follow the documentation closely as we build the Drawer Navigation based application. Along the way, I'll be sharing all the steps I take in detail, as I felt the existing documentation on using the Drawer Navigator from scratch wasn't very clear, in my honest opinion.

I'm excited about this journey, and I hope you'll join me as we explore Expo Router and its functionalities. Feel free to share this content with your friends and associates as well—let's learn and grow together!

The Steps

Run this command to build the app project

npx create-expo-app@latest --template blank@sdk-49  drawer-expo-router
Enter fullscreen mode Exit fullscreen mode

Change into the project directory and install the additional dependencies

npx expo install expo-router@latest react-native-safe-area-context react-native-screens expo-linking expo-constants expo-status-bar react-native-gesture-handler
Enter fullscreen mode Exit fullscreen mode

Add addition dependencies that are needed for the Drawer Navigator

npx expo install @react-navigation/drawer react-native-gesture-handler react-native-reanimated
Enter fullscreen mode Exit fullscreen mode

Open the package.json file and make the following update

{
  "main": "expo-router/entry"
}
Enter fullscreen mode Exit fullscreen mode

Not using web so skipping that part of documentation, but add the scheme app.json

"scheme": "myappdrawersexporouter",
Enter fullscreen mode Exit fullscreen mode

update babel.config.js to include the new plugin for expo-router and an additional one is needed for react-native-reanimated

module.exports = function (api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
    plugins: ['react-native-reanimated/plugin','expo-router/babel'],
  };
};
Enter fullscreen mode Exit fullscreen mode

Lets add the index page and then test it is working, remember the files all live in a new app directory that you have to create. /app/index.js

import { Redirect } from 'expo-router';
import { Text } from 'react-native';

export default function Page() {
  return <Text>INDEX PAGE</Text>;
}
Enter fullscreen mode Exit fullscreen mode

At this point you should be able to run the application and see the index page to confirm the basic installation and configuration of the application is completed

Adding The Drawer

Lets add a group for drawer by creating a folder with the folder name "drawer" in parenthesis, /app/(drawer)

(drawer)
Enter fullscreen mode Exit fullscreen mode

then add a new _layout.js file in the "(drawer)" folder. The directory should look like this /app/(drawer)/_layout.js. We are adding the Drawer UI component and adding one Drawer menu item called "Home".

import { Drawer } from "expo-router";

export default function DrawerLayout() {
  return (
    <Drawer
      screenOptions={{ headerShown: false}}
    >
      <Drawer.Screen
        name="home"
        options={{
          drawerLabel: "Home",
          title: "Home",
        }}
      />
    </Drawer>
  );
}
Enter fullscreen mode Exit fullscreen mode

We are creating the Drawer navigation component with this code and then creating the UI with the following lines of code. This code will create the Drawer with one menu item and associated route home; we will create that route next.

    <Drawer
      screenOptions={{ headerShown: false }}
    >
      <Drawer.Screen
        name="home"
        options={{
          drawerLabel: "Home",
          title: "Home",
        }}
      />
    </Drawer>
Enter fullscreen mode Exit fullscreen mode

Create a new folder name "home" and add a file named index.js to that folder. The new file will be the index page of the Home Drawer Item stack. The directory should look like this /app/(drawer)/home/index.js

import { Text, View, StyleSheet } from "react-native";

export default function Page() {
  return (
    <>
      <View style={styles.container}>
        <Text style={{ fontSize: 24 }}>Index page of Home Drawer</Text>
      </View>
    </>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
});
Enter fullscreen mode Exit fullscreen mode

But we need a layout for the home folder, so add a new file with the contents below. /app/(drawer)/home/_layout.js

import { Stack } from "expo-router";

export default function HomeLayout() {
  return <Stack />;
}
Enter fullscreen mode Exit fullscreen mode

Now we need to update the /app/index.js to redirect to the /app/(drawer)/home at start up.

import { Redirect } from 'expo-router';
import { Text } from 'react-native';

export default function Page() {
  return <Redirect href={"/(drawer)/home"} />;
}
Enter fullscreen mode Exit fullscreen mode

At this point you should be able to run the app and swipe from the left to see the new Drawer with one menu item. Let make some modifications so the Drawer performs as expected.

We are going to add the Drawer menu button adn make it so the Drawer cannot be swiped open. First the menu button.

Open the /app/(drawer)/home/index.js file and add a new import

import { DrawerToggleButton } from "@react-navigation/drawer";
Enter fullscreen mode Exit fullscreen mode

then modify the Drawer.Screen options to include the headerLeft property to show the DrawerToggleButton

    <Drawer.Screen
        options={{
            title: "Home",
            headerShown: true,
            headerLeft: () => <DrawerToggleButton />
        }}
    />
Enter fullscreen mode Exit fullscreen mode

Next to make sure the user cannot swipe the Drawer open, we can modify the options of the Drawer component in the /app/(drawer)/_layout.js file; Set the swipeEdgeWidth to 0.

<Drawer
    screenOptions={{ headerShown: false, swipeEdgeWidth : 0}}
>
Enter fullscreen mode Exit fullscreen mode

Now lets add a second Drawer menu item. To make things easier, just copy the home folder and rename the new copy to settings, then edit the index.js file in the settings folder with the following code.

import { Text, StyleSheet, View } from "react-native";
import { Drawer } from "../_layout";
import { DrawerToggleButton } from "@react-navigation/drawer";

export default function Page() {
  return (
    <View style={styles.container}>
      <Drawer.Screen
        options={{
          title: "Settings",             // <== NEW EDIT HERE
          headerShown: true,
          headerLeft: () => <DrawerToggleButton />,
        }}
      />
      <Text style={{ fontSize: 24 }}>  // <== NEW EDIT HERE
        Index page of Settings Drawer
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
});
Enter fullscreen mode Exit fullscreen mode

I also suggest editing the /app/(drawer)/settings/_layout.js file; just editing the name of the component to SettingsLayout

import { Stack } from "expo-router";

export default function SettingsLayout() {
  return <Stack />;
}
Enter fullscreen mode Exit fullscreen mode

Finally lets add the new Drawer.Screen in /app/(drawer)/_layout.js

export default function DrawerLayout() {
  return (
    <Drawer
      screenOptions={{ headerShown: false, swipeEdgeWidth : 0}}
    >
      <Drawer.Screen
        name="home"
        options={{
          drawerLabel: "Home",
          title: "Home",

        }}
      />
      {/* /// new code below /// */}
      <Drawer.Screen
        name="settings"
        options={{
          drawerLabel: "Settings",
          title: "Settings",
        }}
      />
    </Drawer>
  );
}
Enter fullscreen mode Exit fullscreen mode

Nested Navigation With Home

Create a new page next-page.js in the Home folder; /app/(drawer)/home/next-page.js and add the following code.

import { Stack } from "expo-router";
import { Text, View, StyleSheet } from "react-native";

export default function NextPage() {
  return (
    <View style={styles.container}>
      <Stack.Screen options={{ headerShown: true, title: "Next Page" }} />
      <Text style={{ fontSize: 24 }}>Next page of Home Tab</Text>
    </View>
  );
}


const styles = StyleSheet.create({
    container: {
      flex: 1,
      backgroundColor: "#fff",
      alignItems: "center",
      justifyContent: "center",
    },
  });
Enter fullscreen mode Exit fullscreen mode

To get to that page, we will add a new button to the file /app/(drawer)/home/index.js.

first import Link

import { Link } from "expo-router";
Enter fullscreen mode Exit fullscreen mode

the add the new link to the content section of the page

  <Link href={"/home/next-page"} style={{ marginTop: 16, fontSize: 18 }}>
    <Text style={{ fontWeight: "bold" }}>Go To Next Page</Text>
  </Link>
Enter fullscreen mode Exit fullscreen mode

now you should be able to navigate to next-page and have back button appear in the header to navigate back. In this instance, I am not including the DrawerButton on the next-page so you would need to navigate back to change the menu item.

Wrap

Well I hope you found this helpful, I think it is a bit more detailed than what the documentation provided and it can also be beneficial to those looking for something without typescript.

Links

Social Media

YouTube - https://www.youtube.com/@AaronSaundersCI
Twitter - https://twitter.com/aaronksaunders
Facebook - https://www.facebook.com/ClearlyInnovativeInc
Instagram - https://www.instagram.com/aaronksaunders/
Dev.to - https://dev.to/aaronksaunders

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .