skip to content

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.