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?
answer
- no layout in the folder
- routes hoist to the nearest layout
- names are relative to that layout
- cookbook/desserts, not desserts
- a nested layout means a nested navigator
basics
~20 sWith 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.
solid answer
~40 sExpo Router hoists every route to the **nearest `_layout.tsx`** above it, and the route's name is its path **relative to that layout**. `src/app/cookbook/` has no layout, so `desserts.tsx` becomes a screen of the root stack named `cookbook/desserts`. `name="desserts"` matches no child, so Expo Router logs a `[Layout children]: No route named "desserts"` warning, drops that entry, and the options never apply. The fix is `name="cookbook/desserts"`. Adding `cookbook/_layout.tsx` also works, but it creates a **nested navigator**: the root then sees a single `cookbook` route and the desserts options move into the new layout. Add a nested layout only when the folder needs a different navigator or shared UI; a folder that only shapes URLs should stay layout-free.
code
tsx · 12 lines// src/app/_layout.tsx (no src/app/cookbook/_layout.tsx exists)
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
{/* Name is relative to this layout, so the folder is part of it. */}
<Stack.Screen name="cookbook/desserts" options={{ title: 'Desserts' }} />
</Stack>
);
}go deeper
Recall that a Stack.Screen name must match the route's path relative to its layout, including folder names, and that files without a Stack.Screen still work.
Explain hoisting to the nearest _layout, how that produces names like cookbook/desserts, and how listed and unlisted screens are ordered.
Show you know that adding or removing a layout renames routes and changes the navigator tree, and use the Layout children warnings to catch stale names after refactors.
Set a convention for where screen options live in a large route tree so file moves cannot silently drop options, and when a folder earns its own navigator.
## Routes hoist to the nearest layout Expo Router builds its navigator tree in two passes. First it turns the files under `src/app` into a directory tree. Then it **flattens** that tree: every route file is attached to the **nearest `_layout.tsx` above it**. A folder without its own layout does not create a navigator; its files are simply hoisted into the parent's. In the recipe app: - `src/app/_layout.tsx` returns a `Stack`. - `src/app/cookbook/desserts.tsx` and `src/app/cookbook/weeknight.tsx` exist, but there is **no** `src/app/cookbook/_layout.tsx`. - Both files therefore become screens of the **root stack**. ## Route names are relative to that layout The name of a route inside its layout is its path with the layout's own path removed. For the root layout nothing is removed, so the two screens are named **`cookbook/desserts`** and **`cookbook/weeknight`**. A `Stack.Screen` name must match one of those strings exactly; the one convenience is that naming a folder, such as `cookbook`, also matches `cookbook/index` when that file exists. When the name matches nothing, Expo Router does not throw. It logs `[Layout children]: No route named "desserts" exists in nested children` with the list of real names, skips that entry, and still registers the file with default options. The screen works, but the header title, presentation and other options you wrote are silently absent, which is why the bug often ships unnoticed. ## What Stack.Screen really does `Stack.Screen` and `Tabs.Screen` do **not** create routes; files do. The children of a layout's navigator do three things: 1. **Attach options** (title, header, presentation, tab icon) to an existing route. 2. **Fix the order**: listed screens come first, in the order written. 3. **Leave the rest alone**: unlisted routes are appended automatically, sorted so that `index` and group routes come before other static routes, static routes before dynamic ones, and the not-found route last. Listing more screens than there are routes also warns (`Too many screens defined`), so the warnings are the quickest way to catch a stale name after a file move. ## When to add a nested _layout.tsx Adding `src/app/cookbook/_layout.tsx` changes the tree, not just the names: - The root layout now has a single child route named **`cookbook`** (the folder), and the desserts options belong in the new layout as `<Stack.Screen name="desserts" />`. - If the new layout returns `<Stack />`, you now have a **stack inside a stack**: the cookbook screens push inside their own history, and going back past the folder's first screen pops the whole nested stack off the root. - If it returns `<Slot />`, there is no extra navigator, only shared UI around the child. Expo's guidance is to nest navigators only when truly needed. A folder that only exists to produce a `/cookbook/...` URL prefix should stay layout-free, so its screens keep pushing onto the root stack. | Choice | Root layout's child names | Where `desserts` options go | Navigators | |---|---|---|---| | No `cookbook/_layout.tsx` | `cookbook/desserts`, `cookbook/weeknight` | root, `name="cookbook/desserts"` | one stack | | `cookbook/_layout.tsx` returning `Stack` | `cookbook` | cookbook layout, `name="desserts"` | stack inside a stack | | `cookbook/_layout.tsx` returning `Slot` | `cookbook` | root, one `name="cookbook"` entry for the whole folder | no new navigator | ## A worked example Suppose the recipe app later adds `src/app/cookbook/_layout.tsx` returning a `Stack`, because the cookbook section needs its own header style. Before the change the root layout lists `<Stack.Screen name="cookbook/desserts" options={{ title: 'Desserts' }} />`. After it: 1. The root layout's children become `(tabs)`, `cookbook` and `settings`, so the root entry for `cookbook/desserts` now warns and is skipped. 2. The title must move into `cookbook/_layout.tsx` as `<Stack.Screen name="desserts" options={{ title: 'Desserts' }} />`. 3. The root layout may add `<Stack.Screen name="cookbook" options={{ headerShown: false }} />`, so the root header does not stack on top of the cookbook header. Nothing about the URLs changed, yet three lines of configuration had to move. ## Diagnosing it in a real project - Read the warning's list of names: it prints what the layout actually sees. - Remember that **moving a file across a layout boundary renames its route**, so a refactor that adds or deletes a `_layout.tsx` silently invalidates every `Stack.Screen` name below it. - Keep screen options close to the route when a nested layout exists, rather than configuring deep paths from the root. The underlying model, one navigator per layout and route names relative to it, explains both the warning and the cost of the obvious fix.
- Does a route listed in no Stack.Screen still work in Expo Router?Yes. Files create routes; `Stack.Screen` only adds options and order. Unlisted routes are appended after the listed ones with default options, sorted with index and group routes first and the not-found route last.
- What is the cost of fixing the name by adding src/app/cookbook/_layout.tsx with a Stack?It creates a nested stack. The root layout now sees one `cookbook` route, cookbook screens push inside their own history, and every `Stack.Screen` name that pointed at `cookbook/...` from the root must move into the new layout. Use a nested layout only when the folder needs its own navigator.
saying these in an interview costs you the question
- Stack.Screen creates the route, so an unlisted file cannot be opened.
- Every folder under src/app automatically gets its own navigator.
- A mismatched Stack.Screen name crashes the app at startup.
- Route names in a layout are just the file names without folders.
- Adding a nested _layout.tsx only renames routes and leaves navigation unchanged.