Skip to content

Next.js Project Structure

Next.js is intentionally flexible about how you organize code outside of the routing-specific conventions. This lesson covers common, well-supported ways to structure a growing Next.js project.

The Only Structural Requirement: app/ (or pages/)

Next.js only enforces structure inside app/ (or pages/) — everything else, including where you put shared components, hooks, utilities, and types, is up to you. That flexibility is powerful but means teams benefit from agreeing on conventions early, before the project grows past a handful of routes.

Optionally, your entire project (including app/) can live inside a top-level src/ folder instead of the project root. This is purely organizational — Next.js supports both layouts identically, and create-next-app asks which you prefer during setup.

my-app/
  src/
    app/
      layout.tsx
      page.tsx
    components/
      Button.tsx
    lib/
      db.ts
    hooks/
      useDebounce.ts
    types/
      post.ts
  public/
  next.config.js

Placing everything inside src/ keeps configuration files (like next.config.js) visually separated from application source code.

Common Top-Level Folders

app/          // routes (required location for the App Router)
components/   // shared, reusable UI components
lib/          // server-side utilities, database clients, API wrappers
hooks/        // custom React hooks
types/        // shared TypeScript types
public/       // static assets served from the site root
  • components/ typically holds components shared across multiple routes.
  • lib/ is a common home for database clients, third-party SDK wrappers, and server-only utilities.
  • Route-specific components can be colocated directly inside their route folder instead of components/.
  • public/ assets are served as-is; public/logo.png becomes /logo.png.

Project Structure Cheat Sheet

A reasonable default structure for small-to-medium Next.js apps.

Folder Typical Contents
app/ Routes, layouts, Route Handlers
components/ Shared UI components used across routes
lib/ Database clients, API wrappers, server utilities
hooks/ Custom client-side React hooks
types/ Shared TypeScript interfaces and types
public/ Images, fonts, favicons served statically
styles/ Global CSS, if not colocated with components

Colocation vs. Shared Folders

There are two competing philosophies for organizing components: colocating a component next to the single route that uses it, or centralizing it in components/ if it's reused across multiple routes. Most real projects use both — colocate what's route-specific, centralize what's shared.

Approach Best For
Colocated (app/blog/PostCard.tsx) Components used by exactly one route
Shared (components/Button.tsx) Components reused across many routes

Using Path Aliases

create-next-app configures a TypeScript path alias (commonly @/*) automatically, letting you import from anywhere in the project without long relative paths like ../../../components/Button.

// tsconfig.json
{
  "compilerOptions": {
    "paths": { "@/*": ["./*"] }
  }
}

// usage
import Button from "@/components/Button";

Path aliases keep imports readable as folder nesting grows deeper.

Common Mistakes

  • Placing app/ outside of src/ when the rest of the project expects it inside (or vice versa), causing routes to silently not register.
  • Over-centralizing every component into components/ even when it's only ever used by one route.
  • Not setting up a path alias, leading to long, fragile relative imports as folders get deeper.
  • Mixing unrelated concerns (UI components and server-only database code) into the same folder.

Key Takeaways

  • Next.js only enforces structure inside app/ (or pages/); everything else is your choice.
  • The optional src/ directory purely separates app code from root configuration files.
  • Colocate route-specific components; centralize genuinely shared ones in components/.
  • lib/ is a common convention for server-only utilities like database clients.
  • Path aliases (e.g. @/*) keep imports clean as the project structure grows.

Pro Tip

Agree on a project structure convention with your team before the codebase grows past a few routes — retrofitting a consistent structure onto a large, inconsistent Next.js app is far more work than starting with one.