Back to Blog
Engineering7 min read

Building Scalable Folder Structures in Next.js

Albert Watbin
Dec 2024

Featured Image

The Problem with Default Structures

Every Next.js project starts clean. A few components, a couple of pages, maybe a utility file. Then features grow, pages multiply, and suddenly your components folder is a dumping ground with 87 files and no discernible organization. Sound familiar?

The default Next.js starter gives you almost no opinion about project structure beyond the app/ directory. That's great for flexibility, terrible for teams that need consistency.

The Feature-First Approach

Instead of organizing by file type (components/, utils/, hooks/), organize by feature. Each major feature gets its own folder containing everything it needs to function. This is the single biggest structural change you can make for maintainability.

plaintext
src/
├── features/
│   ├── auth/
│   │   ├── components/
│   │   │   ├── LoginForm.tsx
│   │   │   ├── SignupForm.tsx
│   │   │   └── AuthGuard.tsx
│   │   ├── hooks/
│   │   │   └── useAuth.ts
│   │   ├── utils/
│   │   │   └── tokens.ts
│   │   ├── types/
│   │   │   └── auth.types.ts
│   │   └── index.ts
│   │
│   ├── products/
│   │   ├── components/
│   │   ├── api/
│   │   ├── hooks/
│   │   └── index.ts
│   │
│   └── dashboard/
│       ├── components/
│       ├── hooks/
│       └── index.ts

The key test: can you delete a feature folder and have everything related to that feature disappear cleanly? If yes, your structure is working. If deleting a folder causes cascading breakage across the codebase, your boundaries are wrong.

This approach makes features portable, testable, and — crucially — easier to delete when requirements change. No more archaeology expeditions to find every file related to a feature scattered across 8 directories.

Shared Code Still Has a Home

Not everything belongs to a single feature. Buttons, inputs, API clients, and generic hooks are used everywhere. These live in a shared directory with clear categories:

plaintext
src/
├── shared/
│   ├── ui/          → Buttons, inputs, cards, modals
│   ├── lib/         → API clients, utilities, helpers
│   ├── hooks/       → useWindowSize, useDebounce, useInView
│   └── types/       → Global TypeScript types and interfaces

The rule of thumb: if a piece of code is used by 3+ features, it belongs in shared. If it's used by 1-2 features, keep it local to the primary feature and import where needed.

The App Router Changes Everything

With Next.js 13+ and the App Router, the app/ directory encourages route-based organization. Each route gets its own folder with layout.tsx, page.tsx, loading.tsx, and error.tsx. This is powerful — but it introduces a temptation.

Don't put business logic in page files. Keep pages thin — they should compose components and pass data, nothing more. Move business logic to lib/ or hooks/, and UI to feature components.

What Works at Scale

For a recent platform handling 50,000+ lines of code across 10 developers, this structure saved us measurable time and frustration:

  • ~3 hours/week previously spent on "where does this go?" discussions — eliminated
  • New developer onboarding dropped from 2 weeks to 3 days
  • Git history became dramatically cleaner with feature-based commits
  • Code reviews got faster because reviewers could understand context from folder structure alone

Pro Tips for the Long Haul

  1. 01Use barrel exports (index.ts) to clean up imports — consumers import from the feature, not from deep paths
  2. 02Keep file names consistent across the project. Pick a convention (ProductCard.tsx, not product-card.tsx) and enforce it
  3. 03Add README.md files to complex feature folders explaining the architecture decisions
  4. 04Resist the urge to prematurely abstract. Let patterns emerge from 3+ duplications before creating shared utilities
  5. 05Consider Nx or Turborepo if your project grows into a genuine monorepo with multiple packages

Folder structure isn't glamorous, but it's the foundation everything else is built on. Get it right early and your future self — and every developer who joins after you — will be grateful.

Topics
#Next.js#React#Project Structure#Scalability

Want to discuss scalable systems?

I'm always open to discussing software architecture, platform engineering, or potential collaborations.

Let's Talk
Software Engineer | Full Stack Developer | Scalable Web Platforms