skip to content

Directory Layout Conventions

Files under app/ become routes and each _layout file becomes a navigator, so the file tree is the navigator tree. Interviewers ask how groups, index routes and nested layouts shape URLs.

part ofExpo (React Native)overview, primer and where to startread it →
on this pageshow

explore

questions

6

With Expo Router, how does the src/app directory turn files into routes, and what does a _layout.tsx file add to that tree?

level: juniorimportance: must knowfreq 62%

answer

  1. the file tree is the navigator tree
  2. default export means one screen
  3. path under src/app becomes the URL
  4. _layout renders before its children
  5. no root layout: a generated Slot

basics

~20 s

Each file under src/app with a default export becomes a screen whose URL mirrors its path, and each _layout.tsx wraps its directory's routes in a navigator such as Stack, Tabs or Slot, so the folder tree becomes the navigator tree.

solid answer

~40 s

In Expo Router every file under `src/app` (or a root `app/` in older projects) is a route: its default-exported component is the screen, and its path is its URL, so `src/app/cookbook/desserts.tsx` answers `/cookbook/desserts` on web and as a deep link on Android and iOS. A `_layout.tsx` file is not a screen. It renders before the routes in its directory and decides how they relate: return `<Stack />` for a native stack, `<Tabs />` for bottom tabs, or `<Slot />` to render the current child with no navigator. Layouts nest, so the directory tree is the navigator tree. The root `src/app/_layout.tsx` replaces `App.tsx` for fonts, providers and the splash screen, and shared components live outside `src/app` because anything inside it is treated as a route.

code

tsx · 11 lines
tsx
// src/app/_layout.tsx
import { Stack } from 'expo-router';

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

go deeper

for a junior

Recall the two rules: a file under src/app with a default export is a screen whose path is its URL, and a _layout.tsx wraps its folder's routes in a navigator such as Stack or Tabs.

for a middle

Explain that layouts nest, so the folder tree is the navigator tree, that routes register automatically, and that Stack.Screen exists only for options and ordering.

for a senior

Show you treat the file tree as public API: renaming or moving a route changes URLs that deep links and notifications depend on, and moving it across a layout changes its navigator.

for a principal

Weigh file-based routing against a hand-written navigator config for a large app: URL-first addressability and web parity versus coupling the directory structure to the product's link contract.

## The one rule: the file tree is the route tree **Expo Router** is the file-based router that ships with Expo. Instead of declaring screens in a navigator configuration, you create files. In a project made from the default template (SDK 55 and later) the routes live in **`src/app`**; an older project may still use a root **`app/`** directory, and when both exist Expo Router uses `src/app` only. The rule has three consequences: - **Every file with a default export is a screen.** `src/app/settings.tsx` renders whatever React component it exports as default. - **Every screen has a URL that mirrors its path.** `src/app/cookbook/desserts.tsx` answers `/cookbook/desserts` in a browser, and the same path works as a deep link on Android and iOS once the app has a URL scheme. - **Only the `src/app` prefix and the file extension are dropped.** Folder and file names become URL segments as written, except for the special notations (parentheses, brackets, `_layout`, `+` files). ## `_layout.tsx`: where navigators come from A file named **`_layout.tsx`** is not a page and has no URL of its own. It defines how the routes in its directory are arranged, and it renders **before** them. What it returns decides the navigator: - `<Stack />` from `expo-router` turns the directory's routes into screens of a **native stack**: pushing adds a screen with a header and a back button, and screens underneath stay mounted. - `<Tabs />` (imported from `expo-router/js-tabs` in SDK 57) turns them into tabs of a **JavaScript bottom-tab navigator**. - `<Slot />` renders the current child route in place with **no navigator at all**, which suits wrapping routes in a shared header, footer or provider. Because layouts nest, the **directory tree becomes the navigator tree**. A layout in `src/app` that returns a `Stack` and another in `src/app/(tabs)` that returns `Tabs` give you a stack whose first screen is a tab bar. You do not register screens one by one: every route file in the directory is added automatically, and `Stack.Screen` or `Tabs.Screen` children exist only to set options or order. ## The root layout replaces App.tsx `src/app/_layout.tsx` is the **entry point** of the app. Initialisation that used to live in `App.tsx` goes here: loading fonts, holding and then hiding the splash screen, and mounting context providers such as a theme. If a project has **no root layout**, Expo Router generates one that renders the current route through a `Slot`, so there is no header and no stack history on screen until you write your own. ## What does not belong in src/app Anything under `src/app` is treated as a route, which produces three common mistakes: 1. Putting a shared `RecipeCard.tsx` in `src/app/components/` gives it the URL `/components/RecipeCard`; keep components in `src/components`, hooks in `src/hooks`, constants in `src/constants`. 2. A route file with no default export triggers a development warning that the route is missing its required default export. 3. Config files (`app.json`, `app.config.ts`, `metro.config.js`, `tsconfig.json`, `package.json`) stay at the project root, never inside `src`. ## The recipe app, mapped | File | Role | URL | |---|---|---| | `src/app/_layout.tsx` | root layout returning a `Stack` | none | | `src/app/(tabs)/_layout.tsx` | tab navigator | none | | `src/app/(tabs)/index.tsx` | Recipes tab | `/` | | `src/app/(tabs)/saved.tsx` | Saved tab | `/saved` | | `src/app/cookbook/desserts.tsx` | stack screen | `/cookbook/desserts` | | `src/app/settings.tsx` | stack screen | `/settings` | | `src/app/+not-found.tsx` | fallback for unmatched URLs | any unmatched path | The parentheses in `(tabs)` mark a **route group**, a folder that shapes the navigator tree but adds nothing to the URL; that is why the Saved tab lives at `/saved`, not `/tabs/saved`. ## Why interviewers ask it The question checks whether a candidate sees Expo Router as **URL-first**: every screen is addressable, so deep links and web support fall out of the file layout instead of a separate linking config. The trade-off is that the file system is now public API. Renaming `desserts.tsx` renames a URL that old links, push payloads and bookmarks may still point at, and moving a file across a `_layout.tsx` boundary moves it into a different navigator. A strong answer names both halves: files make routes, layouts make navigators, and the two trees are the same tree.

  • What does Expo Router render if a project has no src/app/_layout.tsx?
    It generates a default root layout that renders the current route through a `Slot`. There is no navigator, so no header, no back button and no stack history until you add a `_layout.tsx` that returns a `Stack`, `Tabs` or another navigator.
  • Why must a shared RecipeCard component live outside src/app?
    Expo Router treats every file under `src/app` as a route, so `src/app/components/RecipeCard.tsx` would get the URL `/components/RecipeCard` and could be navigated to. Components, hooks and constants belong in sibling folders such as `src/components` and are imported by route files.
  • Can an Expo Router project keep its routes in a root app/ directory instead of src/app?
    Yes. Older projects use `app/`, and it still works. When both `app/` and `src/app/` exist, only `src/app` is used. A custom root can be set through the `root` option of the `expo-router` config plugin, but Expo discourages it because tooling assumes one of the two defaults.

A building directory where each door (file) has a room number (URL) and each floor's layout plan (_layout.tsx) decides whether rooms open off a corridor you walk back along (Stack) or off a lobby you switch between (Tabs).

saying these in an interview costs you the question

  • Every screen still has to be registered by hand in a navigator config.
  • A _layout.tsx file is itself a screen with its own URL.
  • Shared components can sit inside src/app without any side effects.
  • A folder without its own _layout.tsx is ignored by the router.
  • Fonts and providers still load in App.tsx next to Expo Router.
open as a page

In Expo Router, how do (group) folders and index.tsx files shape URLs, and how do you make a tab group the app's first screen?

level: middleimportance: must knowfreq 55%

basics

~20 s

A folder named in parentheses, such as (tabs), groups routes under a shared layout without adding a URL segment, and index.tsx is its folder's default route, so src/app/(tabs)/index.tsx answers / and the tab bar becomes the first screen.

open as a page

In Expo Router, what renders when a URL or deep link matches no file under src/app, and how does +not-found.tsx customise it?

level: juniorimportance: should knowfreq 30%

basics

~10 s

Expo Router always adds a not-found route that renders its built-in Unmatched screen; a src/app/+not-found.tsx default export replaces it, and a +not-found.tsx inside a folder handles unmatched URLs under that folder.

open as a page

In Expo Router, why does <Stack.Screen name="desserts"> in the root layout warn 'No route named' when the file is src/app/cookbook/desserts.tsx?

level: middleimportance: should knowfreq 35%

basics

~20 s

With no cookbook/_layout.tsx, the file is hoisted into the root layout under the name cookbook/desserts, so name="desserts" matches nothing; use that relative name, or add a cookbook layout only if the folder needs its own navigator.

open as a page

In an Expo SDK 57 app on Expo Router, why does importing ThemeProvider from '@react-navigation/native' fail to bundle, and what replaces it?

level: seniorimportance: should knowfreq 30%

basics

~10 s

Since SDK 56 expo-router ships its own copy of the React Navigation code and Expo CLI's Metro resolver rejects @react-navigation/* imports from app code; import ThemeProvider from 'expo-router/react-navigation' (or 'expo-router') instead.

open as a page

With Expo Router, a cold-start deep link to /cookbook/desserts opens with no back button; how does exporting unstable_settings.initialRouteName from a _layout fix that?

level: seniorimportance: nice to knowfreq 22%

basics

~20 s

A deep link builds navigation state from the URL alone, so the cookbook stack holds only desserts; exporting unstable_settings = { initialRouteName: 'index' } from cookbook/_layout.tsx makes Expo Router place index beneath it, restoring a back target.

open as a page