📅 Day 12
💡 tip

Clean Folder Structure: Build Apps That Scale

Learn how to organize your React Native/Expo project with a clean, scalable folder structure that makes development easier

ExpoArchitectureBest Practices

Welcome to Day 12 of the React Native Advent Calendar!

Today we’re tackling folder structure - the foundation of every scalable app. A good structure makes your codebase easy to navigate, collaborate on, and maintain as it grows.


Why Folder Structure Matters

Ever opened a project and couldn’t find anything? Spent 10 minutes hunting for a component? That’s the pain of poor organization.

A clean folder structure:

  • Makes onboarding new developers instant
  • Reduces cognitive load (you know exactly where things go)
  • Scales from MVP to enterprise without restructuring
  • Prevents “route confusion” with Expo Router

Why this matters: Your folder structure is like the table of contents of a book. Good organization = faster development.


The Golden Rule: Separation of Concerns

Here’s the #1 principle for Expo/React Native projects:

The app/ folder is ONLY for routes (screens). Everything else lives outside.

Why? Because Expo Router treats every file in app/ as a potential route. If you mix components, hooks, and screens together, chaos ensues.


Here’s a battle-tested folder structure that scales beautifully:

Recommended Expo folder structure

Let’s break down each folder and understand why it exists.


Folder Breakdown

app/ - Routes Only

What goes here: Screens and navigation structure.

What does NOT go here: Reusable components, hooks, utils, or business logic.

Project structure
app/
├── (app)/           # App group (tabs, authenticated screens)
├── api/             # API routes (server endpoints)
├── _layout.tsx      # Root layout
└── +native-intent.ts # Deep linking config

Why: Expo Router uses file-based routing. Every file in app/ becomes a route. Keep it clean and navigable.

components/ - Reusable UI

What goes here: Reusable UI elements like buttons, cards, headers, modals.

Project structure
components/
├── Button.tsx
├── Card.tsx
├── Header.tsx
└── Modal.tsx

Why: Components are the building blocks. Separate them so you can reuse them across multiple screens without duplication.

Pro tip: Organize by feature if your app is large:

Project structure
components/
├── auth/
│   ├── LoginButton.tsx
│   └── SignupForm.tsx
└── products/
    ├── ProductCard.tsx
    └── ProductList.tsx

hooks/ - Custom React Hooks

What goes here: Custom hooks for logic separation, state management, and side effects.

Project structure
hooks/
├── useAuth.ts
├── useApi.ts
└── useTheme.ts

Why: Hooks extract stateful logic from components, making them testable and reusable.

constants/ - App-Wide Values

What goes here: Colors, spacing, API URLs, config values.

Project structure
constants/
├── Colors.ts
├── Sizes.ts
└── Config.ts

Why: Centralize values that never change. Makes theme updates and config changes trivial.

utils/ or services/ - Helper Functions

What goes here: Utility functions, API clients, formatters, validators.

Project structure
utils/
├── api.ts           # API client
├── formatters.ts    # Date/number formatters
└── validators.ts    # Form validation

# OR organize by service:
services/
├── auth/
│   └── authService.ts
└── api/
    └── apiClient.ts

Why: Pure functions that don’t depend on React. Easy to test and reuse.

db/ - Database Logic

What goes here: Database schemas, queries, and migrations (if using local DB like SQLite or Drizzle).

Project structure
db/
├── schema.ts
├── queries.ts
└── migrations/

Why: Separate data layer from UI. Makes database changes isolated.


The src/ Directory (Optional)

Some teams prefer wrapping everything in a src/ folder:

Project structure
src/
├── app/           # Routes
├── components/    # UI components
├── hooks/         # Custom hooks
├── utils/         # Helpers
└── constants/     # Constants

Benefits:

  • Single entry point for all source code
  • Separates code from config files (package.json, app.json, etc.)
  • Cleaner root directory

Expo Router supports this! Just move app/ to src/app/ and it works.


Real-World Example

Here’s how a login screen would be organized:

File locations
app/
└── login.tsx              # The route/screen

components/
└── auth/
    ├── LoginForm.tsx      # Reusable form component
    └── SocialButtons.tsx  # Social login buttons

hooks/
└── useAuth.ts             # Authentication logic

utils/
└── validators.ts          # Email/password validation

constants/
└── Colors.ts              # Theme colors

The login screen (app/login.tsx) stays minimal:

app/login.tsx
import { LoginForm } from '@/components/auth/LoginForm';
import { useAuth } from '@/hooks/useAuth';

export default function LoginScreen() {
	const { signIn } = useAuth();

	return <LoginForm onSubmit={signIn} />;
}

Everything else is extracted and reusable!


Common Mistakes to Avoid

❌ Putting components in app/

Bad
app/
├── login.tsx
└── LoginButton.tsx  # ❌ Expo Router sees this as a route!

Fix: Move components outside app/.

❌ One giant utils/ file

Bad
utils/
└── helpers.ts  # ❌ 2000 lines of random functions

Fix: Split by purpose (formatters.ts, validators.ts, api.ts).

❌ No organization strategy

Bad
components/
├── Button.tsx
├── SuperButton.tsx
├── MyButton.tsx
└── TheButton.tsx  # ❌ Which button do I use?

Fix: Use clear, descriptive names. One Button.tsx with props for variants.


Pro Tips

1. Use Path Aliases

Set up import aliases to avoid ../../../ madness:

tsconfig.json
{
	"compilerOptions": {
		"baseUrl": ".",
		"paths": {
			"@/components/*": ["components/*"],
			"@/hooks/*": ["hooks/*"],
			"@/utils/*": ["utils/*"],
			"@/constants/*": ["constants/*"]
		}
	}
}

Now you can import like this:

app/login.tsx
import { LoginForm } from '@/components/auth/LoginForm'; // ✅ Clean!
// Instead of:
import { LoginForm } from '../../components/auth/LoginForm'; // ❌ Messy

2. Organize by Feature (Large Apps)

For bigger apps, organize by feature instead of type:

Project structure
features/
├── auth/
│   ├── components/
│   ├── hooks/
│   └── utils/
└── products/
    ├── components/
    ├── hooks/
    └── utils/

Wrapping Up

A clean folder structure is the foundation of every great app. Start with good organization, and your codebase will thank you later.

Quick recap:

  1. app/ is for routes ONLY - Keep components, hooks, and utils outside
  2. Separate by concern - components, hooks, constants, utils
  3. Use path aliases - Avoid ../../../ imports
  4. Stay consistent - Pick a structure and stick to it
  5. Scale gradually - Start simple, organize by feature when needed

Want to dive deeper?

Have a favorite folder structure? Share your setup on Twitter!

Tomorrow we’ll explore Day 13’s topic - see you then! 🎄