Next.js 13 Folder Structure Best Practices: Organizing App Router Projects for Scale

The optimal Next.js 13 folder structure places route logic in app/, reusable UI in components/, server‑only utilities in lib/server/, and static assets in public/, while colocating component styles and maintaining a flat public/ directory.

A well‑organized folder hierarchy makes Next.js 13 applications easier to navigate, scale, and maintain according to the conventions used by the framework itself. When examining the vercel/next.js source code, you will find clear separation between framework internals in packages/next/src/ and user‑land patterns that emphasize the App Router, server/client boundaries, and predictable file‑based routing. By aligning your project structure with these internal conventions, you create a codebase that scales naturally with the framework’s evolution.

Adopt the app/ Directory for New Projects

Next.js 13 introduces the App Router (app/) which supports nested layouts, Server Components, and streaming architecture. The framework’s own routing implementation resides in packages/next/src/app/, demonstrating how route folders are resolved at runtime.

Keep each route in its own folder with specific file conventions:

app/
  layout.tsx          # Root layout wrapping all routes

  page.tsx            # Home page (Server Component by default)

  dashboard/
    layout.tsx        # Nested layout for dashboard routes

    page.tsx          # Dashboard page

    loading.tsx       # Loading UI for suspense boundaries

    error.tsx         # Error handling component

  api/
    hello/
      route.ts        # API endpoint (Route Handler)

The legacy pages/ approach still functions, but mixing both routers creates confusion and routing conflicts. Choose one paradigm per project, preferably the App Router for new development.

Group Components with Their Assets

Place a component’s file alongside its CSS Module or Tailwind configuration to reduce deep relative imports. This mirrors the modular layout used by the framework’s UI utilities in packages/next/src/client/components/.

components/
  NavBar/
    NavBar.tsx
    NavBar.module.css
'use client'

import Link from 'next/link'
import styles from './NavBar.module.css'

export default function NavBar() {
  return (
    <nav className={styles.nav}>
      <Link href="/">Home</Link>
      <Link href="/dashboard">Dashboard</Link>
    </nav>
  )
}

This colocation strategy eliminates import path hell and makes components self‑contained units that can be moved between projects.

Isolate Server‑Only Code

Anything running exclusively on the server—database clients, secret handling, or heavy data processing—should live in lib/server/ or use the .server.ts file convention. Next.js treats files ending with .server.tsx as server‑only, preventing accidental client bundling that would leak secrets or bloat the browser bundle.

lib/
  server/
    db.ts             # Server‑only database client

  utils.ts            # Shared utilities

As implemented in packages/next/src/server/, the framework strictly separates server utilities from client components. Emulate this by never importing server‑only modules into client components.

Centralize Custom Hooks and Utilities

Maintain a dedicated hooks/ folder for custom React hooks that encapsulate data fetching or state logic used across components. Combine this with a lib/ directory for pure JavaScript/TypeScript utilities and API clients.

// hooks/useAuth.ts
import { useState, useEffect } from 'react'
import { getSession } from 'next-auth/react'

export function useAuth() {
  const [session, setSession] = useState(null)

  useEffect(() => {
    getSession().then(setSession)
  }, [])

  return session
}

This separation matches the organization found in packages/next/src/lib/, where shared helper functions remain decoupled from React components.

Configure API Routes Consistently

In the App Router, place API endpoints under app/api/ using the route.ts filename convention. For the Pages Router, keep them in pages/api/. Both patterns are reflected in the framework sources under packages/next/src/server/api-utils.

app/
  api/
    users/
      route.ts        # GET, POST handlers for /api/users

Route Handlers in route.ts files replace the older pages/api/ approach when using the App Router exclusively.

Maintain Flat Static Assets

Keep the public/ directory flat rather than deeply nested. Reference assets via /image.png instead of /public/images/image.png. This mirrors how the framework resolves static files in packages/next/src/server/static/ and prevents broken paths during deployment.

public/
  favicon.ico
  logo.png
  hero.jpg

Organize Tests by Feature

Create a mirror of the app/ tree inside tests/ for unit tests, and maintain a separate e2e/ folder for Playwright or Cypress suites. The framework’s own test harness lives under test/ (e.g., test/e2e/, test/unit/), demonstrating how to structure comprehensive coverage alongside source code.

tests/
  dashboard.test.ts   # Unit tests for dashboard logic

e2e/
  navigation.spec.ts  # End‑to‑end user flows

Combine these conventions into a clean, scalable structure:

my-next-app/
├─ app/
│  ├─ layout.tsx          # Root layout with <html>/<body>

│  ├─ page.tsx            # Home page (Server Component)

│  └─ dashboard/
│     ├─ layout.tsx       # Nested layout for dashboard routes

│     └─ page.tsx         # Dashboard page

├─ components/
│  └─ NavBar/
│     ├─ NavBar.tsx
│     └─ NavBar.module.css
├─ hooks/
│  └─ useAuth.ts
├─ lib/
│  └─ server/
│     └─ db.ts            # Server‑only database client

├─ public/
│  └─ favicon.ico
├─ styles/
│  └─ globals.css
├─ tests/
│  └─ dashboard.test.ts
├─ next.config.js
├─ tsconfig.json
└─ package.json

The root layout serves as the entry point for all routes:

import './globals.css'

export const metadata = {
  title: 'My Next.js App',
  description: 'A clean‑structured project',
}

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

Summary

  • Use app/ for new Next.js 13 projects to leverage Server Components and nested layouts, avoiding mixing with pages/.
  • Colocate styles with components in subdirectories to maintain modularity and simplify imports.
  • Isolate server code in lib/server/ or .server.ts files to prevent client bundle pollution.
  • Keep public/ flat for reliable static asset resolution without path nesting.
  • Mirror test structure to the app/ directory for maintainable unit and e2e coverage.
  • Reference internal patterns from packages/next/src/app/ and packages/next/src/server/ to align with framework evolution.

Frequently Asked Questions

Should I use the app/ directory or pages/ in Next.js 13?

You should use the app/ directory for new projects. It provides access to React Server Components, streaming, and nested layouts that the Pages Router cannot support. While pages/ remains maintained for backward compatibility, mixing both routers creates maintenance overhead and potential routing conflicts. Choose one paradigm per project.

How do I prevent server‑only code from leaking to the client?

Place server‑only code in files ending with .server.ts or within a lib/server/ directory, then never import these modules into client components marked with 'use client'. Next.js treats .server suffixed files as server‑exclusive, and bundling errors will warn you if leakage occurs.

Where should API routes live in the App Router?

Place API Route Handlers in app/api/[route]/route.ts files rather than pages/api/. Each HTTP method (GET, POST, etc.) exports a named handler function. This convention is implemented in packages/next/src/server/api-utils and provides better TypeScript inference and co-location with related UI routes.

What is the best way to organize component styles?

Store CSS Modules, Tailwind classes, orStyled‑JSX files in the same folder as the component, typically in a directory named after the component (e.g., components/NavBar/NavBar.tsx and NavBar.module.css). This colocation reduces import complexity and makes components portable between projects without broken style paths.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s https://instagit.com/install.md

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client