ExpoExpo RouterArchitecture

The Best Folder Structure for an Expo Router Project

A clear, scalable folder structure for Expo Router apps — how to organize routes with groups, where to put components and libs, and conventions that keep a growing React Native codebase maintainable.

DeveloperMill2 min read

The best folder structure for an Expo Router project keeps a hard line between routes and everything else: the app/ directory holds only screens and layouts, while components, hooks, and utilities live in sibling folders. Use route groups in parentheses to organize signed-out, tabbed, and modal areas without polluting your URLs. That single rule keeps the project navigable as it grows.

The recommended layout

.
├── app/                      # Routes only — file-based navigation
│   ├── _layout.tsx           # Root layout (providers, theme, auth gate)
│   ├── (auth)/               # Signed-out group — no URL segment
│   │   ├── sign-in.tsx
│   │   └── sign-up.tsx
│   ├── (tabs)/               # Main tabbed app
│   │   ├── _layout.tsx       # Tab bar
│   │   ├── index.tsx         # Home
│   │   └── settings.tsx
│   └── [id].tsx              # Dynamic route
├── components/               # Reusable UI (Button, Card, ...)
│   └── ui/
├── hooks/                    # useAuth, useTheme, ...
├── lib/                      # Clients, helpers, constants
├── constants/                # Colors, config
└── assets/                   # Images, fonts

Why this works

Routes stay obvious

Because app/ contains nothing but routes and _layout files, you can read the folder tree and immediately understand your app's navigation. Mixing components in would make that impossible.

Route groups organize without leaking into URLs

Parentheses folders like (auth) and (tabs) group screens and share a layout, but the group name never appears in the path. This is how you separate the signed-out flow from the main app cleanly.

Layouts compose top-down

Each _layout.tsx wraps the routes beneath it. The root layout is the place for global providers (theme, auth, safe-area); nested layouts define navigators like a stack or tab bar.

Conventions that keep it maintainable

  • One responsibility per layout. Root = providers and the auth gate. (tabs)/_layout = the tab bar. Don't overload them.
  • Co-locate route-specific pieces sparingly. A small component used by exactly one screen can sit next to it, but shared UI belongs in components/.
  • Use a path alias. Configure @/* in tsconfig.json so imports read @/components/ui/Button instead of long relative paths.
  • Keep data access in lib/. Your Supabase or API client lives here, not inside screens.

Naming your identifiers

While you're setting up a new project, you'll also need a valid slug, bundle identifier, and URL scheme for app.json. Generate them from your app name with our free Expo App Config Generator.

A ready-made starting point

Every DeveloperMill Expo template ships with this structure already in place — route groups, a typed @/* alias, and a components/ui kit — so you start from a clean, scalable base instead of an empty app/ folder.

Frequently asked questions

Where do route files go in Expo Router?

Inside the app directory. Every file in app maps to a route, and _layout files define shared UI and navigation for the folders they sit in. Non-route code like components, hooks, and utilities lives outside app.

What are route groups in Expo Router?

Folders wrapped in parentheses, like (auth) or (tabs). They group related routes and let you share a layout without adding a segment to the URL. They're the main tool for organizing signed-in vs signed-out areas of an app.

Should components live inside the app directory?

No. Keep the app directory for routes and layouts only. Put reusable components, hooks, and utilities in sibling folders like components and lib so routing stays clean and predictable.

Build it faster with a template

Production-ready Expo React Native templates with the patterns from this post already wired up.

Browse templates