A comprehensive guide to the Next.js 15 App Router — covering file-system routing, layouts, server components, streaming, and metadata APIs. This practical guide covers step-by-step setup, real-world code snippets, security hardening, and common production edge cases.
Introduction
The Next.js App Router, introduced in Next.js 13 and stabilised in Next.js 14–15, fundamentally changes how you build React applications. Instead of the legacy pages/ directory, you now work inside the app/ directory where every folder maps to a URL segment — and every file inside those folders has a specific role.
This guide walks you through every key concept of the App Router so you can build production-grade Next.js 15 applications confidently.
Prerequisites
- Node.js 18.17 or later
- Familiarity with React and JavaScript/TypeScript
- A basic understanding of HTTP (GET, POST, etc.)
1. Creating a Next.js 15 App
npx create-next-app@latest my-app --typescript --tailwind --app
cd my-app
npm run dev
The --app flag scaffolds the App Router structure. You'll see an app/ directory instead of pages/.
2. File-System Routing
The App Router maps the folder structure directly to URL segments:
| File path | URL |
|---|---|
app/page.tsx | / |
app/about/page.tsx | /about |
app/blog/[slug]/page.tsx | /blog/:slug |
app/shop/[...segments]/page.tsx | /shop/* |
Special files
Each folder can contain these reserved filenames:
page.tsx— the route's UIlayout.tsx— persistent wrapper (doesn't re-render on navigation)loading.tsx— automatic Suspense boundaryerror.tsx— error boundary (must be a Client Component)not-found.tsx— rendered whennotFound()is calledroute.ts— API endpoint (no UI)
3. Layouts and Nested Layouts
Layouts are the most powerful concept in the App Router. A layout renders once and persists across child route navigations — meaning your header, sidebar, or shell won't re-mount.
// app/layout.tsx — root layout
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Header />
{children}
<Footer />
</body>
</html>
);
}
You can nest layouts:
app/
layout.tsx ← wraps everything
dashboard/
layout.tsx ← wraps only /dashboard routes
page.tsx ← /dashboard
settings/
page.tsx ← /dashboard/settings
The dashboard layout can add a sidebar that persists across /dashboard/* without affecting the root layout.
4. Server Components vs Client Components
By default, every component in the app/ directory is a React Server Component (RSC). They:
- Run only on the server
- Can access databases, file systems, and environment variables directly
- Cannot use hooks (
useState,useEffect) or browser APIs - Produce zero JavaScript sent to the client
To opt into a Client Component, add 'use client' at the top of the file:
'use client';
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
The key rule: Client Components can import other Client Components, but they cannot import Server Components. Server Components can import both.
When to use each
| Use Server Component when | Use Client Component when |
|---|---|
| Fetching data | Using useState or useEffect |
| Accessing the database | Handling user events |
| Reading environment variables | Using browser APIs |
| Rendering static content | Integrating third-party libraries that use hooks |
5. Data Fetching in Server Components
Since Server Components run on the server, you can fetch data directly — no useEffect needed:
// app/blog/page.tsx
async function getPosts() {
const res = await fetch('https://api.example.com/posts', {
next: { revalidate: 3600 }, // ISR — revalidate every hour
});
return res.json();
}
export default async function BlogPage() {
const posts = await getPosts();
return (
<div>
{posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
))}
</div>
);
}
Next.js extends the native fetch API with caching options:
{ cache: 'force-cache' }— cache forever (default in some cases){ cache: 'no-store' }— always fetch fresh data{ next: { revalidate: 60 } }— ISR — revalidate after N seconds{ next: { tags: ['posts'] } }— cache tag for on-demand revalidation
6. Streaming with Suspense
The App Router supports React's streaming architecture. Wrap slow components in <Suspense> to show a fallback immediately:
import { Suspense } from 'react';
import SlowComponent from './SlowComponent';
export default function Page() {
return (
<div>
<h1>My Page</h1>
<Suspense fallback={<p>Loading data…</p>}>
<SlowComponent />
</Suspense>
</div>
);
}
Or use the automatic loading.tsx file — Next.js wraps the page.tsx in Suspense automatically:
// app/dashboard/loading.tsx
export default function Loading() {
return <div className="skeleton" />;
}
7. Route Groups and Private Folders
Use route groups (groupName) to organise routes without affecting the URL:
app/
(marketing)/
page.tsx ← /
about/page.tsx ← /about
(app)/
dashboard/
page.tsx ← /dashboard
Both groups can share separate layouts without the group name appearing in the URL.
Private folders start with _ and are excluded from routing:
app/
_components/ ← not a route
_lib/ ← not a route
page.tsx ← /
8. Metadata API
The App Router provides a first-class Metadata API for SEO:
// Static metadata
export const metadata = {
title: 'My Page',
description: 'Page description',
};
// Dynamic metadata
export async function generateMetadata({ params }) {
const post = await getPost(params.slug);
return {
title: post.title,
description: post.description,
openGraph: {
images: [post.coverImage],
},
};
}
9. Route Handlers (API Routes)
Create API endpoints using route.ts files:
// app/api/posts/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
const posts = await fetchPostsFromDB();
return NextResponse.json(posts);
}
export async function POST(request: Request) {
const body = await request.json();
const newPost = await createPost(body);
return NextResponse.json(newPost, { status: 201 });
}
10. Parallel and Intercepting Routes
Parallel routes let you render multiple pages in the same layout simultaneously using slots (@slot):
app/
layout.tsx
@modal/
page.tsx
page.tsx
Intercepting routes let you render a route within the current layout context — perfect for modals that show a photo without losing the feed behind it. Prefix with (.), (..), or (...) depending on how many levels to intercept.
Conclusion
The Next.js App Router is a significant leap forward in how React applications are structured. The key takeaways are:
- Layouts persist across navigations — use them for persistent UI
- Server Components run on the server — use them for data fetching
- Client Components opt into interactivity — use
'use client'when you need hooks - Streaming with Suspense delivers content progressively
- Route Handlers replace the old
pages/api/directory
Mastering these concepts gives you the foundation to build any type of Next.js application — from marketing sites to complex dashboards.
Related Next.js Deep Dives
To continue leveling up your Next.js architecture, check out our companion guides:
- Next.js 15 Caching Strategies Explained — Deep dive into Data Cache, Request Memoization, and
unstable_cache. - Next.js Image Optimization Guide — How to achieve a 100/100 Core Web Vitals score using
next/image. - Server Components vs Client Components — Boundary strategies and bundle size reduction.
- Next.js Parallel & Intercepting Routes — Build modals that preserve URL history.
- Building REST APIs with Next.js Route Handlers — Production API design with TypeScript.
Written by Dev Pattern LabsCore Engineering
Dev Pattern Labs is an engineering lab and digital consultancy specializing in custom Next.js web applications, WooCommerce REST API integrations, and workflow automation for growing businesses.
Recommended Companion Articles

Building REST APIs with Next.js Route Handlers
Learn to build production-ready REST APIs with Next.js Route Handlers. Covers GET, POST, PUT, DELETE methods, NextRequest/NextResponse, query params, CORS, streaming responses, and dynamic segments.

Next.js Middleware: Auth Guards, Redirects & Edge Logic
Master Next.js Middleware for authentication guards, redirects, rewrites, and edge logic. Learn matcher config, cookie-based auth, geolocation redirects, and A/B testing patterns.

Server Components vs Client Components in Next.js: When to Use Each
Learn the fundamental differences between React Server Components and Client Components in Next.js. Understand bundle size impact, data access patterns, composition strategies, and the 'use client' boundary.