Organizing a Scalable Next.js Project
Good project structure becomes critical as applications grow. Next.js is unopinionated about organization outside of the app/ directory, so conventions matter.
Recommended Structure
my-app/
├── app/
│ ├── (auth)/
│ │ ├── login/page.tsx
│ │ └── register/page.tsx
│ ├── (dashboard)/
│ │ ├── layout.tsx
│ │ └── dashboard/page.tsx
│ ├── api/
│ │ └── posts/route.ts
│ ├── layout.tsx
│ └── page.tsx
├── components/
│ ├── ui/ # Low-level UI primitives
│ │ ├── button.tsx
│ │ └── input.tsx
│ └── posts/ # Feature-specific components
│ ├── post-card.tsx
│ └── post-list.tsx
├── lib/
│ ├── db.ts # Database client
│ ├── auth.ts # Auth configuration
│ └── utils.ts # Shared utilities
├── types/
│ └── index.ts # Shared TypeScript types
└── actions/
└── posts.ts # Server Actions
TypeScript Path Aliases
The @/* alias configured in tsconfig.json makes imports clean:
// Without alias (fragile relative paths)
import { Button } from '../../../components/ui/button';
// With alias (always works, regardless of file depth)
import { Button } from '@/components/ui/button';
import { db } from '@/lib/db';
import type { Post } from '@/types';
Colocation Pattern
Keep components close to where they are used. Private components that are only used on one page can live beside that page:
app/
└── dashboard/
├── page.tsx
├── _components/ # Underscore = private, not routable
│ ├── stat-card.tsx
│ └── recent-activity.tsx
└── _lib/
└── queries.ts
Type Definitions
Define shared types in one place:
// types/index.ts
export interface Post {
id: string;
title: string;
slug: string;
content: string;
publishedAt: Date | null;
author: Author;
}
export interface Author {
id: string;
name: string;
email: string;
avatar: string | null;
}
A well-organized project structure reduces cognitive load and makes onboarding new team members significantly faster.