0
# Next.js Core Concepts
1
2
## App Router vs Pages Router
3
4
**App Router (Default)** - Recommended for new projects
5
- React Server Components (default)
6
- Nested layouts with state preservation
7
- Server Actions for mutations
8
- Streaming and Suspense
9
- File-based routing in `app/`
10
11
**Pages Router (Legacy)** - Use for existing apps
12
- Traditional file-based routing in `pages/`
13
- API routes in `pages/api`
14
- Team familiarity with Pages conventions
15
16
**Decision:** New project? → App Router | Existing Pages app? → Continue with Pages
17
18
## Server vs Client Components
19
20
### Server Components (Default)
21
**Use when:**
22
- Fetching data from DB/API
23
- Accessing backend resources
24
- Sensitive information (API keys)
25
- Heavy dependencies (exclude from client bundle)
26
27
**Characteristics:**
28
- Run on server
29
- Can be async
30
- No React hooks
31
- Not in client bundle
32
33
### Client Components
34
**Use when:**
35
- Interactivity (onClick, onChange)
36
- Browser APIs (window, localStorage)
37
- React hooks (useState, useEffect)
38
- Event listeners
39
40
**Characteristics:**
41
- Run in browser
42
- Requires 'use client'
43
- Can use all React features
44
45
```typescript
46
// Server Component (default)
47
export default async function Page() {
48
const data = await fetch('https://api.example.com/posts');
49
const posts = await data.json();
50
return <div>{JSON.stringify(posts)}</div>;
51
}
52
53
// Client Component
54
'use client';
55
import { useState } from 'react';
56
57
export default function Counter() {
58
const [count, setCount] = useState<number>(0);
59
return <button onClick={() => setCount(count + 1)}>{count}</button>;
60
}
61
```
62
63
## Data Fetching Strategies
64
65
### 1. ⚡ Static Generation (SSG) - Fastest
66
**When:** Content doesn't change often
67
**Performance:** Cached at build time, served from CDN edge
68
69
```typescript
70
export const revalidate = false; // Infinite cache
71
72
export default async function Page() {
73
const data = await fetch('https://api.example.com/posts');
74
return <div>{await data.json()}</div>;
75
}
76
```
77
78
### 2. ⚡ Incremental Static Regeneration (ISR) - Great Performance
79
**When:** Content changes but doesn't need to be real-time
80
**Performance:** Cached with periodic revalidation, mostly edge-served
81
82
```typescript
83
export const revalidate = 60; // Revalidate every 60s
84
85
export default async function Page() {
86
const data = await fetch('https://api.example.com/posts');
87
return <div>{await data.json()}</div>;
88
}
89
```
90
91
### 3. ⚠️ Server-Side Rendering (SSR) - Slower
92
**When:** User-specific or frequently changing content
93
**Performance:** Generated on every request, no caching
94
95
```typescript
96
export const dynamic = 'force-dynamic';
97
98
export default async function Page() {
99
const data = await fetch('https://api.example.com/posts', {
100
cache: 'no-store'
101
});
102
return <div>{await data.json()}</div>;
103
}
104
```
105
106
### 4. 🔄 Client-Side Data Fetching - Additional Request
107
**When:** User interactions trigger data fetching
108
**Performance:** Requires separate network request, adds latency
109
110
```typescript
111
'use client';
112
import { useEffect, useState } from 'react';
113
114
interface SearchResult {
115
id: string;
116
title: string;
117
}
118
119
export default function Search() {
120
const [results, setResults] = useState<SearchResult[]>([]);
121
122
useEffect(() => {
123
fetch('/api/search')
124
.then(res => res.json())
125
.then((data: SearchResult[]) => setResults(data));
126
}, []);
127
128
return <div>{results.map(r => r.title).join(', ')}</div>;
129
}
130
```
131
132
### 5. 🔄 Streaming with Suspense - Progressive Rendering
133
**When:** Want to show parts of page while others load
134
**Performance:** Reduces Time to First Byte, improves perceived performance
135
136
```typescript
137
import { Suspense } from 'react';
138
139
export default function Page() {
140
return (
141
<div>
142
<Suspense fallback={<div>Loading...</div>}>
143
<SlowComponent />
144
</Suspense>
145
</div>
146
);
147
}
148
```
149
150
## Caching Strategies
151
152
### Request Deduplication
153
Automatic - multiple identical requests are deduplicated
154
155
### Time-Based Revalidation
156
```typescript
157
const res = await fetch('https://api.example.com/data', {
158
next: { revalidate: 3600 } // 1 hour
159
});
160
```
161
162
### Tag-Based Revalidation
163
```typescript
164
// Fetch with tag
165
const posts = await fetch('https://api.example.com/posts', {
166
next: { tags: ['posts'] }
167
});
168
169
// Revalidate by tag
170
import { revalidateTag } from 'next/cache';
171
revalidateTag('posts');
172
```
173
174
### On-Demand Revalidation
175
```typescript
176
import { revalidatePath } from 'next/cache';
177
revalidatePath('/posts');
178
```
179
180
## Route File Conventions
181
182
```
183
app/
184
page.tsx → /
185
about/page.tsx → /about
186
blog/[slug]/page.tsx → /blog/[slug]
187
(marketing)/about/page.tsx → /about (route group)
188
```
189
190
### Dynamic Segments
191
```
192
[id] → Single dynamic segment
193
[...slug] → Catch-all
194
[[...slug]] → Optional catch-all
195
```
196
197
### Route Groups
198
```
199
(marketing)/about/page.tsx → /about (parentheses not in URL)
200
```
201
202
### Parallel Routes
203
```
204
app/
205
layout.tsx
206
@analytics/
207
page.tsx → Analytics slot
208
```
209
210
## Navigation Patterns
211
212
### Client-Side
213
```typescript
214
'use client';
215
import { useRouter, usePathname } from 'next/navigation';
216
217
const router = useRouter();
218
router.push('/about');
219
```
220
221
### Server-Side
222
```typescript
223
import { redirect } from 'next/navigation';
224
225
export default async function Page() {
226
const user = await getUser();
227
if (!user) redirect('/login');
228
return <div>Content</div>;
229
}
230
```
231
232
## Error Handling
233
234
```typescript
235
// app/error.tsx
236
'use client';
237
export default function Error({
238
error,
239
reset
240
}: {
241
error: Error & { digest?: string };
242
reset: () => void;
243
}) {
244
return (
245
<div>
246
<h2>Error: {error.message}</h2>
247
<button onClick={reset}>Try again</button>
248
</div>
249
);
250
}
251
252
// app/not-found.tsx
253
import { notFound } from 'next/navigation';
254
255
export default async function Post({
256
params
257
}: {
258
params: Promise<{ id: string }>
259
}) {
260
const { id } = await params;
261
const post = await getPost(id);
262
if (!post) notFound();
263
return <article>{post.content}</article>;
264
}
265
```
266
267
## Metadata & SEO
268
269
```typescript
270
import type { Metadata } from 'next';
271
272
export const metadata: Metadata = {
273
title: 'Page Title',
274
description: 'Description',
275
openGraph: {
276
title: 'OG Title',
277
images: ['/og-image.jpg'],
278
},
279
};
280
281
// Dynamic
282
export async function generateMetadata({
283
params
284
}: {
285
params: Promise<{ id: string }>
286
}): Promise<Metadata> {
287
const { id } = await params;
288
const post = await getPost(id);
289
return { title: post.title };
290
}
291
```
292
293
## Performance Optimization
294
295
### Image
296
```typescript
297
import Image from 'next/image';
298
299
<Image src="/photo.jpg" alt="Photo" width={800} height={600} priority />
300
```
301
302
### Font
303
```typescript
304
import { Inter } from 'next/font/google';
305
import type { ReactNode } from 'react';
306
307
const inter = Inter({ subsets: ['latin'], variable: '--font-inter' });
308
309
export default function Layout({ children }: { children: ReactNode }) {
310
return <html className={inter.variable}><body>{children}</body></html>;
311
}
312
```
313
314
### Script
315
```typescript
316
import Script from 'next/script';
317
318
<Script src="https://example.com/script.js" strategy="afterInteractive" />
319
```
320
321
## Middleware
322
323
```typescript
324
import { NextResponse } from 'next/server';
325
import type { NextRequest } from 'next/server';
326
327
export function middleware(request: NextRequest) {
328
const token = request.cookies.get('token');
329
330
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
331
return NextResponse.redirect(new URL('/login', request.url));
332
}
333
334
return NextResponse.next();
335
}
336
337
export const config = { matcher: '/dashboard/:path*' };
338
```
339
340
## Key Decisions
341
342
1. **App Router or Pages?** → App Router for new projects
343
2. **Server or Client?** → Server by default, Client when needed
344
3. **Static or Dynamic?** → Static for performance, Dynamic for fresh data
345
4. **Time or Tag revalidation?** → Time for predictable, Tag for user-generated
346
5. **Link or router.push?** → Link for navigation, router.push for programmatic
347