Expo Router Fundamentals Best Practices
A condensed summary of the 25 most important best practices drawn from every page in this section.
Search across all documentation pages
A condensed summary of the 25 most important best practices drawn from every page in this section.
Scaffold with Expo Router from day one: default@sdk-57 sets main: "expo-router/entry" - retrofitting file-based routing costs more than starting with app/.
Install router through Expo: npx expo install expo-router pins versions compatible with SDK 57's React Navigation peers - manual npm ranges cause native/JS skew.
Treat file names as URLs: app/settings.tsx → /settings - design paths for deep links and analytics before folders proliferate.
Keep route files thin: Under ~20 lines; re-export screens from features/<name>/ - reviewers grep app/ to understand navigation, not business rules.
One root _layout.tsx for providers: Theme, query client, auth session, and error boundaries mount once - never duplicate QueryClientProvider in tab layouts.
Nested layouts own chrome only: Stack headers, tab icons, drawer labels - not data fetching. useFocusEffect refetch belongs in screens.
Use route groups for structure: (auth), (app), (tabs), (modals) - parentheses organize without polluting URLs.
Private folders use underscore prefix: _components/ colocated with layouts is not routable - never create accidental /components screens.
Index routes for list hubs: orders/index.tsx lists; orders/[id].tsx details - predictable REST-like paths aid linking.
Single redirect hub at app/index.tsx: Auth and role routing live in one place - avoid duplicate / index files across groups.
Wait for session hydration before redirect: Show splash or spinner while SecureStore reads - premature <Redirect> causes login/home flicker.
router.replace after login and logout: Preserves back-stack hygiene - push leaves credential screens behind swipe-back.
Guard authenticated groups in layout: (app)/_layout.tsx redirects guests - deep links to /settings must not bypass auth.
Prefer Link for declarative navigation: Lists, inline links, and prefetch - reserve router.push for post-async side effects.
Centralize href builders: orderDetailHref(id) in navigation/hrefs.ts - one typo fix updates every feature callsite.
Enable typed routes early: experiments: { typedRoutes: true } in app.config.ts - invalid paths fail tsc, not production.
Validate params with Zod at runtime: useLocalSearchParams() input is untrusted from deep links and push payloads - types alone are insufficient.
Nest stacks inside tabs for drill-down: One tab pushes detail screens while the tab bar stays - cap nesting at drawer → tabs → stack.
Hide non-tab routes with href: null: Helper screens stay navigable via router.push without cluttering the tab bar.
Modal routes are explicit: (modals) group or presentation: "modal" - half-overlays break Android back and accessibility.
Declare scheme before shipping deep links: app.config.ts scheme and associated domains pair with file paths - test Maestro flows per critical URL.
Run tsc --noEmit when app/ changes: Regenerated .expo/types catches stale hrefs - add to CI alongside expo-doctor.
Align app/ with feature slices: features/orders/ maps to app/(tabs)/orders/ - import rule: routes import features, not the reverse.
Document non-standard patterns in ADRs: Modals as overlays, custom tab bars, or React Navigation escape hatches - before the third squad copies them.
Revisit route map at ~30 screens: Flatten deep trees, split modals, and audit duplicate paths - schedule before import cycles and duplicate / routes force a rewrite.
In features/<name>/ - hooks, API, and screen UI. app/ files re-export only. Global providers live in root _layout.tsx or app-providers.tsx.
app/*.tsx unless auth redirect guards.npx expo start --non-interactive &
npm run typecheckEnsure .expo/types generates before tsc in CI when app/ changes.
app/ - (auth) and private foldersHref typesStack versions: This page was written for React 19.2.3, React Native 0.86.0, and Expo SDK 57 (
expo~57.0.4).
Reviewed by Chris St. John·Last updated Jul 16, 2026