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
Recommended Minimal Project Layout
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 withpages/. - Colocate styles with components in subdirectories to maintain modularity and simplify imports.
- Isolate server code in
lib/server/or.server.tsfiles 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/andpackages/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:
curl -s https://instagit.com/install.md