or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

api.mdconcepts.mdindex.mdpages-router.mdpatterns.mdspecial-files.md

concepts.mddocs/

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