Skip to main content

Next.js Project Structure

When you start working with Next.js, understanding how the project is structured is crucial for efficient development. In this guide, we'll explore the standard project structure of a Next.js application, explain the purpose of each directory and file, and provide best practices for organizing your code.

Introduction

Next.js provides an opinionated but flexible project structure that helps you organize your code in a way that's both maintainable and scalable. The framework uses file-system based routing and has specific conventions for directories and files that serve special purposes.

Understanding this structure will help you:

  • Navigate your codebase efficiently
  • Know where to place specific components and functionality
  • Follow Next.js conventions for optimal performance
  • Leverage built-in features more effectively

Let's dive into what makes up a typical Next.js application.

Basic Next.js Project Structure

When you create a new Next.js project using create-next-app, you'll get a directory structure that looks something like this:

my-next-app/
├── node_modules/
├── public/
├── src/
│ ├── app/
│ │ ├── favicon.ico
│ │ ├── globals.css
│ │ ├── layout.js
│ │ └── page.js
│ ├── components/
│ └── styles/
├── .eslintrc.json
├── .gitignore
├── jsconfig.json
├── next.config.js
├── package.json
└── README.md

Let's examine each part of this structure in detail.

Core Directories and Files

/public Directory

The public directory is used for static assets that should be served as-is:

public/
├── favicon.ico
├── images/
│ ├── logo.png
│ └── banner.jpg
└── fonts/

Key points:

  • Files inside public can be referenced from the root of your application
  • Great for storing images, fonts, and other static assets
  • Files are served at the route / by Next.js

Example:

jsx
// How to reference an image from the public directory
function MyComponent() {
// Note that the path is relative to the public directory
return <img src="/images/logo.png" alt="Logo" />;
}

/src Directory

The src directory is optional but recommended for organizing application code:

src/
├── app/ // For App Router (Next.js 13+)
├── pages/ // For Pages Router (traditional)
├── components/
├── lib/
└── styles/

Using a src directory helps keep your project root cleaner and is a common practice in React applications.

/app Directory (App Router - Next.js 13+)

In Next.js 13 and later, the App Router uses a directory called app:

app/
├── favicon.ico
├── globals.css
├── layout.js
├── page.js
├── about/
│ └── page.js
└── blog/
├── layout.js
├── page.js
└── [slug]/
└── page.js

Key points:

  • layout.js defines shared layouts
  • page.js defines the unique UI of a route
  • Folders define the route structure
  • Special files like loading.js, error.js, and not-found.js handle specific states

Example of a basic page component:

jsx
// app/about/page.js
export default function AboutPage() {
return (
<main>
<h1>About Us</h1>
<p>Welcome to our Next.js application!</p>
</main>
);
}

/pages Directory (Pages Router - Traditional)

In the traditional Pages Router approach, routing is handled through the pages directory:

pages/
├── index.js
├── about.js
├── _app.js
├── _document.js
├── api/
│ └── hello.js
└── blog/
├── index.js
└── [slug].js

Key points:

  • Each .js, .jsx, .ts, or .tsx file becomes a route
  • index.js becomes the root route
  • _app.js and _document.js are special files for customization
  • Nested folders create nested routes
  • The api folder contains API routes

Example of a basic page:

jsx
// pages/about.js
export default function About() {
return (
<div>
<h1>About Us</h1>
<p>This is the about page</p>
</div>
);
}

/components Directory

The components directory is for your React components:

components/
├── Button/
│ ├── index.js
│ └── Button.module.css
├── Navbar/
│ ├── index.js
│ └── Navbar.module.css
└── Footer.js

Example of a reusable component:

jsx
// components/Button/index.js
import styles from './Button.module.css';

export default function Button({ children, onClick }) {
return (
<button className={styles.button} onClick={onClick}>
{children}
</button>
);
}

/styles Directory

The styles directory holds your CSS files:

styles/
├── globals.css
└── Home.module.css

Next.js supports various styling methods including CSS Modules, Sass, styled-jsx, and more.

Configuration Files

├── next.config.js
├── package.json
├── jsconfig.json
└── .eslintrc.json

Key files:

  • next.config.js: Configuration options for Next.js
  • jsconfig.json or tsconfig.json: Configures JavaScript/TypeScript settings
  • .eslintrc.json: ESLint configuration

Example of a basic next.config.js:

js
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
images: {
domains: ['example.com'],
},
}

module.exports = nextConfig

Special Directories and Files

API Routes

In the Pages Router, API endpoints are defined in the pages/api directory:

pages/api/
├── hello.js
└── users/
├── index.js
└── [id].js

Example of a simple API route:

js
// pages/api/hello.js
export default function handler(req, res) {
res.status(200).json({ message: 'Hello from Next.js!' })
}

In the App Router, API routes are defined in the app/api directory as Route Handlers:

js
// app/api/hello/route.js
export async function GET() {
return Response.json({ message: 'Hello from Next.js!' })
}

Data Fetching and Server Components

In Next.js 13+ with the App Router, components are Server Components by default:

jsx
// app/users/page.js
async function getUsers() {
const res = await fetch('https://api.example.com/users');
return res.json();
}

export default async function UsersPage() {
const users = await getUsers();

return (
<div>
<h1>Users</h1>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}

Real-World Project Structure Example

For larger applications, you might structure your project like this:

my-next-app/
├── public/
│ ├── images/
│ └── fonts/
├── src/
│ ├── app/
│ │ ├── layout.js
│ │ ├── page.js
│ │ ├── (auth)/ # Route group
│ │ │ ├── login/
│ │ │ └── register/
│ │ ├── dashboard/
│ │ └── api/
│ ├── components/
│ │ ├── ui/
│ │ │ ├── Button.js
│ │ │ └── Card.js
│ │ ├── layout/
│ │ │ ├── Navbar.js
│ │ │ └── Footer.js
│ │ └── features/
│ │ └── ProductList.js
│ ├── lib/
│ │ ├── utils.js
│ │ └── api-client.js
│ ├── hooks/
│ │ └── useUser.js
│ └── styles/
│ ├── globals.css
│ └── variables.css
├── next.config.js
└── package.json

This structure organizes code by:

  • Technical concern (components, hooks, lib)
  • Feature or domain (auth, dashboard)
  • Component type (ui, layout, features)

Best Practices for Project Structure

  1. Group by feature or route for large applications

    // Group related features together
    src/features/authentication/
    src/features/dashboard/
  2. Keep components organized by type or feature

    // By type
    components/ui/
    components/layout/

    // Or by feature
    components/auth/
    components/products/
  3. Use barrel exports for cleaner imports

    js
    // components/ui/index.js
    export { default as Button } from './Button';
    export { default as Card } from './Card';

    // Then import like this:
    import { Button, Card } from '@/components/ui';
  4. Leverage route groups in the App Router by using parentheses

    app/
    ├── (marketing)/ # Route group (not included in URL path)
    │ ├── about/
    │ └── contact/
    └── (shop)/ # Another route group
    ├── products/
    └── cart/
  5. Use path aliases for cleaner imports

    js
    // jsconfig.json
    {
    "compilerOptions": {
    "baseUrl": ".",
    "paths": {
    "@/components/*": ["src/components/*"],
    "@/lib/*": ["src/lib/*"]
    }
    }
    }

Migrating Between Structure Patterns

If you're moving from the Pages Router to the App Router, you can have both in the same project:

my-next-app/
├── app/ # App Router (Next.js 13+)
└── pages/ # Pages Router (traditional)

Next.js will use the App Router for matching routes first, then fall back to the Pages Router.

Summary

Understanding Next.js project structure is essential for working efficiently with the framework:

  • The /public directory is for static assets
  • The /app directory (in Next.js 13+) uses file-system based routing with a more flexible structure
  • The /pages directory (traditional approach) also uses file-system based routing
  • Special files like layout.js, page.js, _app.js, and _document.js serve specific purposes
  • Organizing components, styles, and utilities in separate directories helps maintain a clean structure

As your application grows, consider organizing by feature or domain while maintaining a consistent structure throughout your project.

Additional Resources

Exercises

  1. Create a new Next.js project using create-next-app and explore the generated directory structure.
  2. Reorganize a simple Next.js application to use a feature-based structure.
  3. Create a shared layout for multiple pages using the App Router's layout.js file.
  4. Set up path aliases in jsconfig.json or tsconfig.json to simplify imports in your project.
  5. Implement an API route in both the Pages Router and App Router approaches and compare the differences.

Happy coding with Next.js!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)