Expo UI and Router for iOSExpo SDK 57 and later, iOS only
Expo Router

Core concepts

Learn the ground rules of Expo Router and how it relates to the rest of your code.

Before diving into how to construct your app's navigation tree with Expo Router, let's first understand the core concepts that make up the foundation of file-based routing in Expo Router, and how an Expo Router project might differ in structure from other React Native projects.

The rules of Expo Router

1. All screens/pages are files inside the src/app directory

All navigation routes in your app are defined by the files and sub-directories inside the src/app directory. Every file inside the src/app directory has a default export that defines a distinct page in your app (except for the special _layout files).

Accordingly, directories inside src/app define groups of related screens together.

2. All pages have a URL

All pages have a URL path that matches the file's location in the src/app directory, which can be used to navigate to that page in the address bar on the web, or as an app-specific deep link in a native mobile app.

  • This is what is meant by Expo Router supporting universal deep-linking.
  • All pages in your app can be navigated to with a URL, regardless of the platform.

3. First index.tsx is the initial route

With Expo Router, you do not define an initial route or first screen in code.

  • Rather, when you open your app, Expo Router will look for the first index.tsx file matching the / URL.
  • In the default template, this is src/app/index.tsx.
  • If the app user should start by default in a deeper part of your navigation tree, you can use a route group (a directory where the name is surrounded in parentheses), and that will not count as part of the URL.
  • If you want your first screen to be a group of tabs, you might put all of the tab pages inside the src/app/(tabs) directory and define the default tab as index.tsx.
  • With this arrangement, the / URL will take the user directly to src/app/(tabs)/index.tsx file.

4. Root _layout.tsx replaces App.jsx/tsx

Every project should have a _layout.tsx file directly inside the src/app directory.

  • This file is rendered before any other route in your app and is where you would put the initialization code that may have previously gone inside an App.jsx file, such as loading fonts, setting up theme providers, or interacting with the splash screen.
  • For example, the default template wraps the app with a ThemeProvider for dark and light mode support and renders the AppTabs component from this file.

5. Default template uses platform-specific tabs

The default template uses two different tab implementations depending on the platform.

  • On Android and iOS, tabs are rendered using native tabs, which use the platform's built-in tab bar for a native look and feel.

This is achieved through platform-specific file extensions.

  • The tab component is defined in two files: src/components/app-tabs.native.tsx for Android and iOS, and src/components/app-tabs.tsx for web.
  • Expo's module resolution automatically picks the correct file based on the platform.
  • This pattern is used because native platforms have a system tab bar that provides expected behaviors like scroll-to-top on tap and native animations, while web requires a custom-styled tab bar that fits typical website navigation patterns.

6. Non-navigation components live outside the src/app directory

In Expo Router, the src/app directory is exclusively for defining your app's routes.

  • Other parts of your app, like components, hooks, utilities, and so on, should be placed in other directories such as src/components, src/hooks, and src/constants.
  • If you put a non-route inside the src/app directory, Expo Router will attempt to treat it like a route.

7. Customizing stack and tab navigators

Stack and tab navigators accept a wide range of configuration options for headers, animations, gestures, and more. See the Stack and Tabs guides for the full list of options and examples.

The rules of Expo Router applied

Let's apply these foundational rules of Expo Router to quickly identify key elements of the following project file structure:

  • src
    • app
      • index.tsx
      • home.tsx
      • _layout.tsx
      • profile
        • friends.tsx
    • components
      • app-tabs.native.tsx
      • app-tabs.tsx
      • text-field.tsx
      • toolbar.tsx
  • src/app/index.tsx is the initial route, and will appear first when you open the app or navigate to your web app's root URL.
  • src/app/home.tsx is a page with the route /home, so you can navigate to it with a URL like yourapp.com/home in the browser, or yourapp://home in a native app.
  • src/app/_layout.tsx is the root layout. Any initialization code you may have previously put in App.jsx should go here.
  • src/app/profile/friends.tsx is a page with the route /profile/friends.
  • src/components/app-tabs.native.tsx and src/components/app-tabs.tsx are platform-specific tab components. The app-tabs.native.tsx file is used on Android and iOS, while the app-tabs.tsx file is used on web. The root layout imports these to render the tab navigator.
  • src/components/text-field.tsx and src/components/toolbar.tsx are not in the src/app directory, so they will not be considered pages. They will not have a URL, and they cannot be the target of a navigation action. However, they can be used as components in the pages inside the src/app directory.

Notation

When you look inside the src/app directory in a typical Expo Router project, you'll see a lot more than some simple file and directory names.

  • What do the parentheses and brackets mean?
  • Let's learn the significance of file-based routing notation and how it allows you to define complex navigation patterns.

Types of route notation

Simple names/no notation

  • src
    • app
      • home.tsx
      • feed
        • favorites.tsx

Regular file and directory names without any notation signify static routes. Their URL matches exactly as they appear in your file tree. So, a file named favorites.tsx inside the feed directory will have a URL of /feed/favorites.

Square brackets

  • src
    • app
      • [userName].tsx
      • products
        • [productId]
          • index.tsx

If you see square brackets in a file or directory name, you are looking at a dynamic route.

  • The name of the route includes a parameter that can be used when rendering the page.
  • The parameter could be either in a directory name or a file name.
  • For example, a file named [userName].tsx will match /evanbacon, /expo, or another username.
  • Then, you can access that parameter with the useLocalSearchParams hook inside the page, using that to load the data for that specific user.

Parentheses

  • src
    • app
      • (home)
        • index.tsx
        • settings.tsx

A directory with its name surrounded in parentheses indicates a route group.

  • These directories are useful for grouping routes together without affecting the URL.
  • For example, a file named src/app/(home)/settings.tsx will have /settings for its URL, even though it is not directly in the src/app directory.

Route groups can be useful for simple organization purposes, but often become more important for defining complex relationships between routes.

index.tsx files

  • src
    • app
      • (home)
        • index.tsx
      • profile
        • index.tsx

Just like on the web, an index.tsx file indicates the default route for a directory.

  • For example, a file named profile/index.tsx will match /profile.
  • A file named (home)/index.tsx will match /, effectively becoming the default route for your entire app.

_layout.tsx files

  • src
    • app
      • _layout.tsx
      • (home)
        • _layout.tsx
      • feed
        • _layout.tsx

_layout.tsx files are special files that are not pages themselves but define how groups of routes inside a directory relate to each other. If a directory of routes is arranged as a stack or tabs, the layout route is where you would define that relationship by using a stack navigator or tab navigator component.

Layout routes are rendered before the actual page routes inside their directory. This means that the _layout.tsx directly inside the src/app directory is rendered before anything else in the app, and is where you would put the initialization code that may have previously gone inside an App.jsx file.

Plus sign

  • src
    • app
      • +not-found.tsx
      • +html.tsx
      • +native-intent.tsx
      • +middleware.ts

Routes that include a + have special significance to Expo Router, and are used for specific purposes. A few examples:

  • +not-found, which catches any requests that don't match a route in your app.
  • +html is used to customize the HTML boilerplate used by your app on web.
  • +native-intent is used to handle deep links into your app that don't match a specific route, such as links generated by third-party services.
  • +middleware is used to run code before a route is rendered, allowing you to perform tasks like authentication or redirection for every request.

Some path names such as /assets are reserved by Metro and Expo Router. Avoid using them for routes. See Reserved paths for the complete list.

Route notation applied

Consider the following project file structure to identify the different types of routes represented:

  • src
    • app
      • (home)
        • _layout.tsx
        • index.tsx
        • feed.tsx
        • profile.tsx
      • _layout.tsx
      • users
        • [userId].tsx
      • +not-found.tsx
      • about.tsx
  • src/app/about.tsx is a static route that matches /about.
  • src/app/users/[userId].tsx is a dynamic route that matches /users/123, /users/456, and so on.
  • src/app/(home) is a route group. It will not factor into the URL, so /feed will match src/app/(home)/feed.tsx.
  • src/app/(home)/index.tsx is the default route for the (home) directory, and will match the / URL.
  • src/app/(home)/_layout.tsx is a layout file defining how the pages inside src/app/(home)/ relate to each other.
  • src/app/_layout.tsx is the root layout file, and is rendered before any other route in the app.
  • src/app/+not-found.tsx is a special route that will be displayed if the user navigates to a route that doesn't exist in your app.

Top-level src directory

Projects created with the default template on SDK 55 and later already include a top-level src directory that contains the app, components, constants, and hooks directories. No extra configuration is needed.

If you are using a custom template or an existing project that doesn't include a src directory, follow the steps below to set it up.

Using a top-level src directory

Move your app directory to src/app.

  • src
    • app
      • _layout.tsx
      • index.tsx
    • components
      • button.tsx
  • package.json

Update TypeScript path aliases in the tsconfig.json file to point to the src directory instead of the root directory. If you use the default @/* alias, set it to ./src/*:

{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

This keeps @/ imports working after moving your app directory into src.

Restart your development server.

bun expo start
# Or export for production
bun expo export

Notes

  • The config files (app.config.ts, app.json, package.json, metro.config.js, tsconfig.json) should remain in the root directory.
  • The src/app directory takes higher precedence than the root app directory. Only the src/app directory will be used if you have both.
  • The public directory should remain in the root directory.
  • Static rendering will automatically use the src/app directory if it exists.
  • You may consider updating any type aliases to point to the src directory instead of the root directory.

Custom directory

Changing the default root directory is highly discouraged. We will not accept bug reports regarding projects with custom root directories.

You can dangerously customize the root directory using the Expo Router Config Plugin. The following will change the root directory to src/routes, relative to the project root.

{
  "plugins": [
    [
      "expo-router",
      {
        "root": "./src/routes"
      }
    ]
  ]
}

This may lead to unexpected behavior. Many tools assume the root directory to be either app or src/app. Only tools in the exact version of Expo CLI will respect the config plugin.

Reserved paths

If you create a route or place static files at certain URL paths, Metro or Expo Router will intercept the request instead of serving your content. Depending on the path, this can result in a "404 Asset not found" error or your page being silently replaced by an internal dev server response.

/assets/*

Metro serves all bundled assets (images, fonts, and other files) at this path. If you create a route at app/assets.tsx or a directory at public/assets/, Metro intercepts the request and your content is never reached.

This applies to both top-level routes and static files:

  • app
    • assets.tsxConflicts with Metro
    • assets
      • index.tsxConflicts with Metro
  • public
    • assets
      • logo.pngConflicts with Metro

Rename your route or directory to avoid the conflict:

  • app
    • media.tsxWorks
  • public
    • images
      • logo.pngWorks

/_expo/*

Expo Router uses this path for multiple internal middlewares, including dev tools and manifests. Do not create routes or static files under this path.

/_flight/*

React Server Components use this path internally. Do not create routes or static files under this path.

/inspector

React Native uses /inspector/debug and /inspector/network for the debugger. Avoid creating routes that match /inspector or its sub-paths.

/expo-dev-plugins/*

Expo development tool plugins use this path. Do not create routes or static files under this path.

/manifest

The dev server serves the native app manifest at this path. If you create a route at app/manifest.tsx, the dev server responds with manifest JSON instead of your page. Your route will appear to silently not load during development.

/_sitemap

Expo Router automatically generates a sitemap route at this path for debugging. If you create a route at app/_sitemap.tsx, it will override the built-in sitemap. See Sitemap for more details on this feature.

/public/*

If your project has a public directory, the /public URL path may conflict with static file serving. Avoid creating a route at app/public.tsx or app/public/index.tsx since the path is implicitly reserved when the public directory exists.

/favicon.ico

Unlike the paths above, /favicon.ico is safe to override. Expo CLI serves a default favicon when none is provided. You can replace it by placing a favicon.ico file in your public directory or by creating an API route.

Sources

The original Expo docs for this page:

On this page