With Expo Router, how does the src/app directory turn files into routes, and what does a _layout.tsx file add to that tree?
answer
- the file tree is the navigator tree
- default export means one screen
- path under src/app becomes the URL
- _layout renders before its children
- no root layout: a generated Slot
basics
~20 sEach 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 sIn 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// 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
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.
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.
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.
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.