Create pages with Expo Router

Edit this page

Learn about the file-based routing convention used by Expo Router.


When a file is created in the app directory, it automatically becomes a route in the app. For example, the following files will create the following routes:

app
index.tsxmatches '/'
home.tsxmatches '/home'
[user].tsxmatches dynamic paths like '/expo' or '/evanbacon'
settings
  index.tsxmatches '/settings'

Pages

Pages are defined by exporting a React component as the default value from a file in the app directory. The file they are exported from must use one of the .js, .jsx, .tsx, .ts extensions.

For example, create the app directory in your project and then create a file index.tsx inside it. Then, add the following snippet:

Render text on any platform with the <Text> component from React Native.

app/index.tsx
import { Text } from 'react-native';

export default function Page() {
  return <Text>Top-level page</Text>;
}

Alternatively, you can write web-only React components such as <div>, <p>, and so on. However, these won't render on native platforms.

app/index.tsx
export default function Page() {
  return <p>Top-level page</p>;
}

The above example matches the / route in the app and the browser. Files named index match the parent directory and do not add a path segment. For example, app/settings/index.tsx matches /settings in the app.

Platform specific extensions

Platform-specific extensions were added in Expo Router 3.5.x. If you are using an older version of the library, follow instructions from Platform-specific modules.

Metro bundler's platform-specific extensions (for example, .ios.tsx or .native.tsx) are supported in the app directory only if a non-platform version also exists. This ensures that routes are universal across platforms for deep linking.

Consider the following project structure:

app
_layout.tsx
_layout.web.tsx
index.tsx
about.tsx
about.web.tsx

In the above file structure:

  • _layout.web.tsx file is used as a layout on the web and _layout.tsx is used on all other platforms.
  • index.tsx file is used as the home page for all platforms.
  • about.web.tsx file is used as the about page for the web, and the about.tsx file is used on all other platforms.
Providing a route file without a platform-specific extension is required to ensure every platform has a default implementation.

Dynamic routes

Dynamic routes match any unmatched path at a given segment level.

RouteMatched URL
app/blog/[slug].tsx/blog/123
app/blog/[...rest].tsx/blog/123/settings

Routes with higher specificity will be matched before a dynamic route. For example, /blog/bacon will match blog/bacon.tsx before blog/[id].tsx.

Multiple slugs can be matched in a single route by using the rest syntax (...). For example, app/blog/[...id].tsx matches /blog/123/settings.

Dynamic segments are accessible as route parameters in the page component.

app/blog/[slug].tsx
import { useLocalSearchParams } from 'expo-router';
import { Text } from 'react-native';

export default function Page() {
  const { slug } = useLocalSearchParams();

  return <Text>Blog post: {slug}</Text>;
}

Non-route files

Every file and sub-directory inside the app directory is either a _layout file or a route in your app. Other files, such as components, hooks, utilities, and so on, cannot be placed in the app directory because they are neither screens nor layout files.

app
(home)
  _layout.tsx
  profile.tsx/profile
(modal)
  _layout.tsx
  profile.tsx/profile

The above example uses shared routes for the /profile route inside two sub-directories. If you want to create a non-route file (for example, ProfileImageComponent.tsx), finding the correct sub-directory to place this file creates a dilemma since there are two route directories. This is "why" Expo Router does not allow non-route files to exist with route files inside the app directory because it leads to an unstructured project.

Recommended structure

Expo Router recommends sorting components and hooks by "feature" and routes by "navigation pattern". While the two concepts can overlap, there are enough differences to structure them differently. Additionally, routes will inevitably change. However, components generally stay within the same feature set. Keeping routes separate will simplify your refactors.

app
sign-in
sign-out
profile
tasks
components
authentication
tasks
  overview
profile
  [user]

In the above example, both /sign-in and /sign-out are authentication routes and share components from the components/authentication file. However, the /profile/[user] route is complex and renders components that belong to the authentication, profile, and task features. If you place your components within the app directory, it's not obvious where these components belong. By separating components from routes, you create an organized hierarchy with the philosophy that a route is a collection of reusable components, and a component does not belong to an individual route.

Another project optimization is to utilize path aliases. They keep your routes project structure agnostic, as imports within your routes no longer depend on the app directory structure. This avoids large import statement changes when adding new directories (such as new groups) and prevents lengthy import paths.