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.
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.
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.
You now know how to structure a Next.js project. Next, look at next.config.js, the file that controls framework-level configuration.