Now that you've created your first Next.js project, let's take a comprehensive tour of its structure. Understanding where everything goes and why will help you build applications confidently and follow Next.js conventions. By the end of this lesson, you'll know exactly what every file does and where to put your own code.
Project Structure Overview
A Next.js 15 project has a very organized structure. Let's look at the complete file tree first, then dive into each part:
Complete Next.js Project Structure
Click on any file or folder to learn its purpose
Select a file or folder to see details
The structure might seem overwhelming at first, but there are really only two directories you'll work in regularly:
- app/ - Where all your pages, routes, and layouts live
- public/ - Where you store static assets like images
Everything else is configuration or auto-generated. Let's understand each part!
The app Directory - Heart of Your Application
The app directory is where the magic happens. This is where you'll spend most of your time as a Next.js developer.
Why It's Called "App Router"
The App Router gets its name from this app directory. Everything inside app follows the App Router conventions and uses React Server Components by default. This is different from the older "Pages Router" which used a pages directory.
Key Principle: Folders = Routes
In Next.js, your folder structure is your routing structure. Each folder can potentially become a route in your URL.
How Folders Become Routes
See the direct connection between folders and URLs
📁 File Structure
app/ page.tsx
🌐 URL Path
/The root page.tsx creates your homepage at the / route.
🗂️ Folder Organization
Not every folder becomes a route! Only folders with a page.tsx file are publicly accessible. Folders withoutpage.tsx are just for organization.
Special Files in the app Directory
Next.js uses special file names with specific purposes. These files have "superpowers" that regular files don't have:
page.tsx - Creates a Route
Purpose: Makes a route segment publicly accessible
Required? Yes, if you want the route to be accessible
// This creates the /about route
export default function AboutPage() {
return (
<div>
<h1>About Us</h1>
<p>This is the about page.</p>
</div>
);
}Without page.tsx, the folder is just for organization and won't create a public route.
layout.tsx - Shared UI
Purpose: Creates UI that wraps child pages and layouts
Required? Yes for root, optional for other routes
import type { Metadata } from 'next';
import './globals.css';
export const metadata: Metadata = {
title: 'My Next.js App',
description: 'Built with Next.js 15',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
{/* This wraps ALL pages */}
<nav>Navigation here</nav>
{children}
<footer>Footer here</footer>
</body>
</html>
);
}Key points:
- Root layout must include
<html>and<body>tags - Layouts don't re-render when navigating between pages
- You can nest layouts for different sections
- State persists in layouts during navigation
loading.tsx - Loading UI
Purpose: Automatic loading state with Suspense
Required? No, but very useful
export default function Loading() {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="animate-spin rounded-full h-32 w-32 border-b-2 border-gray-900" />
<p className="ml-4">Loading dashboard...</p>
</div>
);
}This automatically shows while the page is loading! Next.js wraps your page with React Suspense boundaries automatically.
error.tsx - Error Boundaries
Purpose: Catch and handle errors gracefully
Required? No, but recommended for production
'use client'; // Error components must be Client Components
import { useEffect } from 'react';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
// Log error to error reporting service
console.error(error);
}, [error]);
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>
Try again
</button>
</div>
);
}Note: Error components must be Client Components (use "use client").
not-found.tsx - 404 Pages
Purpose: Custom 404 not found page
Required? No, Next.js has a default
import Link from 'next/link';
export default function NotFound() {
return (
<div className="flex flex-col items-center justify-center min-h-screen">
<h1 className="text-6xl font-bold mb-4">404</h1>
<h2 className="text-2xl mb-4">Page Not Found</h2>
<p className="text-gray-600 mb-8">
The page you're looking for doesn't exist.
</p>
<Link
href="/"
className="px-6 py-3 bg-blue-600 text-white rounded-lg"
>
Go Home
</Link>
</div>
);
}route.ts - API Routes
Purpose: Create API endpoints (backend)
Required? Only if you need API endpoints
// GET /api/users
export async function GET() {
const users = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
];
return Response.json(users);
}
// POST /api/users
export async function POST(request: Request) {
const body = await request.json();
// Save to database...
return Response.json(
{ message: 'User created', user: body },
{ status: 201 }
);
}Important: If a folder has route.ts, it cannot have page.tsx. It's one or the other!
template.tsx - Similar to Layout but Re-renders
Purpose: Like layout but creates new instance on navigation
Required? No, rarely used
Use template.tsx when you need state to reset on navigation, otherwise use layout.tsx.
Special File Naming Rules
- These special files must be named exactly as shown (lowercase)
- They must have the
.tsxor.tsextension - They must
export defaulta component (except metadata) - Typos won't work:
Page.tsxorpages.tsxwon't create routes
Organizing Code in the app Directory
Beyond special files, how should you organize your components, utils, and other code? Here are the common patterns:
1. Colocation - Keep Related Code Together
You can put any files in the app directory. Only special files like page.tsx are public.
app/
dashboard/
page.tsx ← Public route /dashboard
DashboardHeader.tsx ← Component used by page
DashboardCard.tsx ← Another component
utils.ts ← Helper functions
types.ts ← TypeScript types2. Private Folders - Using Underscore
Folders starting with _ are private and won't become routes:
app/
_components/ ← Private folder (won't create route)
Button.tsx
Card.tsx
_lib/ ← Private folder
utils.ts
api.ts
page.tsx ← Public route /3. Route Groups - Organizing Without URL Segments
Folders in parentheses (name) organize routes without affecting the URL:
app/
(marketing)/ ← Route group (not in URL)
about/
page.tsx ← /about (not /marketing/about)
contact/
page.tsx ← /contact
(shop)/ ← Another route group
products/
page.tsx ← /products
cart/
page.tsx ← /cartUse route groups to:
- Organize routes into logical groups
- Apply different layouts to different sections
- Keep your file structure clean without affecting URLs
4. Recommended: Separate Components Folder
Many developers create a separate components folder at the root for shared components:
my-next-app/
app/ ← Routes and pages
page.tsx
about/
page.tsx
components/ ← Shared components
ui/
Button.tsx
Card.tsx
layout/
Header.tsx
Footer.tsx
lib/ ← Shared utilities
utils.ts
db.ts💡 Organization is Flexible
There's no single "right" way to organize your Next.js project. Choose a structure that makes sense for your team and project size. Start simple and refactor as your app grows!
The public Directory
The public folder is where you store static assets that don't need processing:
public/
images/
logo.png
hero.jpg
fonts/
custom-font.woff2
robots.txt
sitemap.xml
favicon.icoAccessing Public Files
Files in public are served from the root URL path:
// In your components
import Image from 'next/image';
export default function Header() {
return (
<div>
{/* Reference files from public at root path */}
<Image src="/images/logo.png" alt="Logo" width={200} height={50} />
{/* robots.txt is at /robots.txt */}
{/* Not /public/robots.txt */}
</div>
);
}Important Public Directory Rules
- Files are served at root path:
/public/logo.pngbecomes/logo.png - Don't name files the same as routes (e.g., don't create
public/aboutif you haveapp/about) - Only files in
publicat build time are served - For images you're importing in code, you can also put them in
app
Configuration Files
Let's understand the important configuration files in your project root:
next.config.ts - Next.js Configuration
This file customizes Next.js behavior:
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
// Allow images from external domains
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'example.com',
},
],
},
// Redirects
async redirects() {
return [
{
source: '/old-blog/:slug',
destination: '/blog/:slug',
permanent: true,
},
];
},
// Environment variables available in browser
env: {
CUSTOM_KEY: 'my-value',
},
};
export default nextConfig;package.json - Project Metadata
Defines dependencies and scripts. The key scripts you'll use:
{
"scripts": {
"dev": "next dev --turbopack", // Development server
"build": "next build", // Production build
"start": "next start", // Start production server
"lint": "next lint" // Check code quality
},
"dependencies": {
"next": "^15.0.0", // Next.js version
"react": "^19.0.0", // React version
"react-dom": "^19.0.0"
}
}tsconfig.json - TypeScript Configuration
Next.js has set this up perfectly. The key settings you might care about:
{
"compilerOptions": {
"paths": {
"@/*": ["./*"] // Allows @/app/... imports
}
}
}This lets you write clean imports like import Button from '@/components/Button' instead of relative paths like ../../../components/Button.
.env.local - Environment Variables
Store secrets and configuration here:
# Database
DATABASE_URL="postgresql://..."
# API Keys (server-side only)
API_SECRET_KEY="secret123"
# Public variables (exposed to browser)
NEXT_PUBLIC_API_URL="https://api.example.com"Environment Variable Security
- Server-only: Variables without
NEXT_PUBLIC_prefix - Browser-exposed: Variables with
NEXT_PUBLIC_prefix - Never commit
.env.localto Git! - It's already in
.gitignoreby default
Auto-Generated Folders
These folders are automatically created and should never be edited manually:
.next Directory
Contains the build output. Created when you run npm run dev or npm run build.
- Never edit files here
- Safe to delete - regenerates automatically
- Already in .gitignore
node_modules Directory
Contains all installed npm packages.
- Created by
npm install - Can be deleted and reinstalled anytime
- Already in .gitignore
- Can be very large (hundreds of MB)
Complete Project Example
Let's see a realistic project structure for a blog application:
Blog Application Structure
A practical example showing pages, layouts, and API routes
Select a file or folder to see details
This structure creates these routes:
/- Homepage (app/page.tsx)/about- About page (app/about/page.tsx)/blog- Blog listing (app/blog/page.tsx)/blog/my-post- Individual post (app/blog/[slug]/page.tsx)/api/users- API endpoint (app/api/users/route.ts)
Best Practices for Project Structure
1. Start Simple, Refactor Later
Don't over-organize early. Begin with a flat structure and add folders as you need them. Premature organization can slow you down.
2. Colocate Related Code
Keep components, utilities, and types close to where they're used. This makes code easier to find and maintain.
3. Use Consistent Naming
- Components: PascalCase (Button.tsx, UserCard.tsx)
- Utilities: camelCase (formatDate.ts, api.ts)
- Special files: Lowercase (page.tsx, layout.tsx)
4. Group by Feature, Not by Type
Good: Organize by feature/domain
app/
dashboard/
page.tsx
DashboardChart.tsx
useDashboardData.ts
profile/
page.tsx
ProfileForm.tsx
updateProfile.tsLess ideal: Organize by file type
app/
components/
DashboardChart.tsx
ProfileForm.tsx
hooks/
useDashboardData.ts
utils/
updateProfile.tsThis makes it harder to find related code when working on a feature.
5. Create a lib Folder for Shared Code
my-next-app/
lib/ ← Shared utilities
db.ts ← Database client
auth.ts ← Authentication helpers
utils.ts ← General utilities
constants.ts ← App-wide constantsCommon Structure Mistakes to Avoid
❌ Don't create unnecessary nesting
// Too deep for no reason
app/
pages/ ← Unnecessary folder
home/
index/
page.tsx ← Just use app/page.tsx❌ Don't mix special files incorrectly
app/
api/
users/
page.tsx ← ERROR! Can't have both
route.ts ← page.tsx and route.ts❌ Don't ignore naming conventions
app/
about/
Page.tsx ← Won't work! Must be lowercase
pages.tsx ← Won't work! Must be singular❌ Don't put everything in the root
my-next-app/
component1.tsx ← Keep components organized
component2.tsx ← in proper folders
utils.ts
helper.tsWhat NOT to Commit to Git
Your .gitignore file is already set up, but here's what it excludes and why:
# Dependencies
node_modules/ # Large, can be reinstalled
# Build output
.next/ # Generated files
out/ # Export output
# Environment variables
.env.local # Contains secrets!
.env*.local
# IDE files
.vscode/ # Editor settings (optional)
.idea/
# OS files
.DS_Store # Mac system files
Thumbs.db # Windows thumbnails
# Logs
npm-debug.log*
yarn-debug.log*🔒 Security First
Never commit .env.local or any file containing API keys, passwords, or secrets. These belong only on your local machine and your deployment platform.
Key Takeaways
- The
appdirectory is where all routes and pages live - Folder structure in
appcreates URL routes - Special files like
page.tsx,layout.tsxhave specific purposes - Only folders with
page.tsxare publicly accessible - The
publicfolder contains static assets served at root path - Configuration files like
next.config.tscustomize Next.js behavior - Never edit
.nextornode_modulesmanually - Use
.env.localfor secrets (never commit to Git) - Organize by feature/domain rather than by file type
- Start simple and add structure as your app grows
What's Next?
Now that you understand the project structure and where everything goes, you're ready to learn about one of Next.js's most important features: the difference between the App Router and Pages Router.
In the next lesson, we'll compare these two routing systems, explain why the App Router is the future of Next.js, and make sure you understand why this tutorial series focuses exclusively on the App Router approach.
📁 Practice Exercise
Try creating this structure in your Next.js project:
- Create an
app/about/page.tsxfile - Add a simple component that says "About Us"
- Navigate to
http://localhost:3000/about - See your new page appear automatically!