@jgamaraalv/ts-dev-kit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +24 -0
- package/.claude-plugin/plugin.json +24 -0
- package/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +128 -0
- package/agents/accessibility-pro.md +139 -0
- package/agents/api-builder.md +110 -0
- package/agents/code-reviewer.md +190 -0
- package/agents/database-expert.md +138 -0
- package/agents/debugger.md +241 -0
- package/agents/docker-expert.md +51 -0
- package/agents/multi-agent-coordinator.md +378 -0
- package/agents/nextjs-expert.md +136 -0
- package/agents/performance-engineer.md +138 -0
- package/agents/playwright-expert.md +126 -0
- package/agents/react-specialist.md +97 -0
- package/agents/security-scanner.md +105 -0
- package/agents/test-generator.md +221 -0
- package/agents/typescript-pro.md +253 -0
- package/agents/ux-optimizer.md +93 -0
- package/docs/rules/orchestration.md.template +126 -0
- package/package.json +28 -0
- package/skills/bullmq/SKILL.md +225 -0
- package/skills/bullmq/references/flows-and-schedulers.md +186 -0
- package/skills/bullmq/references/job-types-and-options.md +163 -0
- package/skills/bullmq/references/patterns.md +273 -0
- package/skills/bullmq/references/production.md +308 -0
- package/skills/composition-patterns/SKILL.md +58 -0
- package/skills/composition-patterns/references/architecture-avoid-boolean-props.md +87 -0
- package/skills/composition-patterns/references/architecture-compound-components.md +107 -0
- package/skills/composition-patterns/references/patterns-children-over-render-props.md +77 -0
- package/skills/composition-patterns/references/patterns-explicit-variants.md +87 -0
- package/skills/composition-patterns/references/react19-no-forwardref.md +37 -0
- package/skills/composition-patterns/references/state-context-interface.md +194 -0
- package/skills/composition-patterns/references/state-decouple-implementation.md +96 -0
- package/skills/composition-patterns/references/state-lift-state.md +126 -0
- package/skills/conventional-commits/SKILL.md +148 -0
- package/skills/docker/SKILL.md +55 -0
- package/skills/docker/references/compose-configs.md +95 -0
- package/skills/docker/references/monorepo-dockerfile.md +111 -0
- package/skills/drizzle-pg/SKILL.md +202 -0
- package/skills/drizzle-pg/references/advanced.md +299 -0
- package/skills/drizzle-pg/references/migrations.md +214 -0
- package/skills/drizzle-pg/references/queries.md +321 -0
- package/skills/drizzle-pg/references/relations.md +272 -0
- package/skills/drizzle-pg/references/schema-pg.md +256 -0
- package/skills/drizzle-pg/references/sql-operator.md +215 -0
- package/skills/fastify-best-practices/SKILL.md +143 -0
- package/skills/fastify-best-practices/references/hooks-and-lifecycle.md +122 -0
- package/skills/fastify-best-practices/references/plugins-and-encapsulation.md +137 -0
- package/skills/fastify-best-practices/references/request-reply-errors.md +189 -0
- package/skills/fastify-best-practices/references/routes-and-handlers.md +134 -0
- package/skills/fastify-best-practices/references/server-and-options.md +127 -0
- package/skills/fastify-best-practices/references/typescript-and-logging.md +223 -0
- package/skills/fastify-best-practices/references/validation-and-serialization.md +190 -0
- package/skills/ioredis/SKILL.md +51 -0
- package/skills/ioredis/references/advanced-patterns.md +312 -0
- package/skills/ioredis/references/cluster-sentinel.md +280 -0
- package/skills/ioredis/references/connection-options.md +187 -0
- package/skills/ioredis/references/core-api.md +179 -0
- package/skills/nextjs-best-practices/SKILL.md +194 -0
- package/skills/nextjs-best-practices/references/async-patterns.md +84 -0
- package/skills/nextjs-best-practices/references/bundling.md +192 -0
- package/skills/nextjs-best-practices/references/data-patterns.md +310 -0
- package/skills/nextjs-best-practices/references/debug-tricks.md +127 -0
- package/skills/nextjs-best-practices/references/directives.md +74 -0
- package/skills/nextjs-best-practices/references/error-handling.md +237 -0
- package/skills/nextjs-best-practices/references/file-conventions.md +152 -0
- package/skills/nextjs-best-practices/references/font.md +175 -0
- package/skills/nextjs-best-practices/references/functions.md +116 -0
- package/skills/nextjs-best-practices/references/hydration-error.md +86 -0
- package/skills/nextjs-best-practices/references/image.md +184 -0
- package/skills/nextjs-best-practices/references/metadata.md +305 -0
- package/skills/nextjs-best-practices/references/parallel-routes.md +299 -0
- package/skills/nextjs-best-practices/references/route-handlers.md +154 -0
- package/skills/nextjs-best-practices/references/rsc-boundaries.md +168 -0
- package/skills/nextjs-best-practices/references/runtime-selection.md +40 -0
- package/skills/nextjs-best-practices/references/scripts.md +148 -0
- package/skills/nextjs-best-practices/references/self-hosting.md +210 -0
- package/skills/nextjs-best-practices/references/suspense-boundaries.md +67 -0
- package/skills/owasp-security-review/SKILL.md +98 -0
- package/skills/owasp-security-review/references/a01-broken-access-control.md +78 -0
- package/skills/owasp-security-review/references/a02-security-misconfiguration.md +81 -0
- package/skills/owasp-security-review/references/a03-supply-chain-failures.md +65 -0
- package/skills/owasp-security-review/references/a04-cryptographic-failures.md +82 -0
- package/skills/owasp-security-review/references/a05-injection.md +106 -0
- package/skills/owasp-security-review/references/a06-insecure-design.md +76 -0
- package/skills/owasp-security-review/references/a07-authentication-failures.md +83 -0
- package/skills/owasp-security-review/references/a08-integrity-failures.md +72 -0
- package/skills/owasp-security-review/references/a09-logging-alerting-failures.md +76 -0
- package/skills/owasp-security-review/references/a10-exceptional-conditions.md +131 -0
- package/skills/postgresql/SKILL.md +50 -0
- package/skills/postgresql/references/ddl-schema.md +300 -0
- package/skills/postgresql/references/indexes.md +257 -0
- package/skills/postgresql/references/jsonb.md +261 -0
- package/skills/postgresql/references/performance.md +291 -0
- package/skills/postgresql/references/psql-cli.md +153 -0
- package/skills/postgresql/references/queries.md +287 -0
- package/skills/postgresql/references/transactions.md +280 -0
- package/skills/react-best-practices/SKILL.md +110 -0
- package/skills/react-best-practices/references/advanced-patterns.md +91 -0
- package/skills/react-best-practices/references/async-patterns.md +233 -0
- package/skills/react-best-practices/references/bundle-optimization.md +201 -0
- package/skills/react-best-practices/references/client-patterns.md +178 -0
- package/skills/react-best-practices/references/js-performance.md +210 -0
- package/skills/react-best-practices/references/rendering-performance.md +209 -0
- package/skills/react-best-practices/references/rerender-optimization.md +316 -0
- package/skills/react-best-practices/references/server-performance.md +274 -0
- package/skills/service-worker/SKILL.md +195 -0
- package/skills/service-worker/references/api-reference.md +114 -0
- package/skills/service-worker/references/caching-strategies.md +202 -0
- package/skills/service-worker/references/push-and-sync.md +261 -0
- package/skills/typescript-conventions/SKILL.md +51 -0
- package/skills/ui-ux-guidelines/SKILL.md +105 -0
- package/skills/ui-ux-guidelines/references/accessibility-and-interaction.md +74 -0
- package/skills/ui-ux-guidelines/references/forms-content-checklist.md +126 -0
- package/skills/ui-ux-guidelines/references/layout-typography-animation.md +95 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# Error Handling
|
|
2
|
+
|
|
3
|
+
Handle errors gracefully in Next.js applications.
|
|
4
|
+
|
|
5
|
+
Reference: https://nextjs.org/docs/app/getting-started/error-handling
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Error Boundaries](#error-boundaries)
|
|
10
|
+
- [Server Actions: Navigation API Gotcha](#server-actions-navigation-api-gotcha)
|
|
11
|
+
- [Redirects](#redirects)
|
|
12
|
+
- [Auth Errors](#auth-errors)
|
|
13
|
+
- [Not Found](#not-found)
|
|
14
|
+
- [Error Hierarchy](#error-hierarchy)
|
|
15
|
+
|
|
16
|
+
## Error Boundaries
|
|
17
|
+
|
|
18
|
+
### `error.tsx`
|
|
19
|
+
|
|
20
|
+
Catches errors in a route segment and its children:
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
"use client";
|
|
24
|
+
|
|
25
|
+
export default function Error({
|
|
26
|
+
error,
|
|
27
|
+
reset,
|
|
28
|
+
}: {
|
|
29
|
+
error: Error & { digest?: string };
|
|
30
|
+
reset: () => void;
|
|
31
|
+
}) {
|
|
32
|
+
return (
|
|
33
|
+
<div>
|
|
34
|
+
<h2>Something went wrong!</h2>
|
|
35
|
+
<button onClick={() => reset()}>Try again</button>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Important:** `error.tsx` must be a Client Component.
|
|
42
|
+
|
|
43
|
+
### `global-error.tsx`
|
|
44
|
+
|
|
45
|
+
Catches errors in root layout:
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
"use client";
|
|
49
|
+
|
|
50
|
+
export default function GlobalError({
|
|
51
|
+
error,
|
|
52
|
+
reset,
|
|
53
|
+
}: {
|
|
54
|
+
error: Error & { digest?: string };
|
|
55
|
+
reset: () => void;
|
|
56
|
+
}) {
|
|
57
|
+
return (
|
|
58
|
+
<html>
|
|
59
|
+
<body>
|
|
60
|
+
<h2>Something went wrong!</h2>
|
|
61
|
+
<button onClick={() => reset()}>Try again</button>
|
|
62
|
+
</body>
|
|
63
|
+
</html>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Important:** Must include `<html>` and `<body>` tags.
|
|
69
|
+
|
|
70
|
+
## Server Actions: Navigation API Gotcha
|
|
71
|
+
|
|
72
|
+
**Do NOT wrap navigation APIs in try-catch.** They throw special errors that Next.js handles internally.
|
|
73
|
+
|
|
74
|
+
Reference: https://nextjs.org/docs/app/api-reference/functions/redirect#behavior
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
'use server'
|
|
78
|
+
|
|
79
|
+
import { redirect } from 'next/navigation'
|
|
80
|
+
import { notFound } from 'next/navigation'
|
|
81
|
+
|
|
82
|
+
// Bad: try-catch catches the navigation "error"
|
|
83
|
+
async function createPost(formData: FormData) {
|
|
84
|
+
try {
|
|
85
|
+
const post = await db.post.create({ ... })
|
|
86
|
+
redirect(`/posts/${post.id}`) // This throws!
|
|
87
|
+
} catch (error) {
|
|
88
|
+
// redirect() throw is caught here - navigation fails!
|
|
89
|
+
return { error: 'Failed to create post' }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Good: Call navigation APIs outside try-catch
|
|
94
|
+
async function createPost(formData: FormData) {
|
|
95
|
+
let post
|
|
96
|
+
try {
|
|
97
|
+
post = await db.post.create({ ... })
|
|
98
|
+
} catch (error) {
|
|
99
|
+
return { error: 'Failed to create post' }
|
|
100
|
+
}
|
|
101
|
+
redirect(`/posts/${post.id}`) // Outside try-catch
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Good: Re-throw navigation errors
|
|
105
|
+
async function createPost(formData: FormData) {
|
|
106
|
+
try {
|
|
107
|
+
const post = await db.post.create({ ... })
|
|
108
|
+
redirect(`/posts/${post.id}`)
|
|
109
|
+
} catch (error) {
|
|
110
|
+
if (error instanceof Error && error.message === 'NEXT_REDIRECT') {
|
|
111
|
+
throw error // Re-throw navigation errors
|
|
112
|
+
}
|
|
113
|
+
return { error: 'Failed to create post' }
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Same applies to:
|
|
119
|
+
|
|
120
|
+
- `redirect()` - 307 temporary redirect
|
|
121
|
+
- `permanentRedirect()` - 308 permanent redirect
|
|
122
|
+
- `notFound()` - 404 not found
|
|
123
|
+
- `forbidden()` - 403 forbidden
|
|
124
|
+
- `unauthorized()` - 401 unauthorized
|
|
125
|
+
|
|
126
|
+
Use `unstable_rethrow()` to re-throw these errors in catch blocks:
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
import { unstable_rethrow } from "next/navigation";
|
|
130
|
+
|
|
131
|
+
async function action() {
|
|
132
|
+
try {
|
|
133
|
+
// ...
|
|
134
|
+
redirect("/success");
|
|
135
|
+
} catch (error) {
|
|
136
|
+
unstable_rethrow(error); // Re-throws Next.js internal errors
|
|
137
|
+
return { error: "Something went wrong" };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Redirects
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
import { redirect, permanentRedirect } from "next/navigation";
|
|
146
|
+
|
|
147
|
+
// 307 Temporary - use for most cases
|
|
148
|
+
redirect("/new-path");
|
|
149
|
+
|
|
150
|
+
// 308 Permanent - use for URL migrations (cached by browsers)
|
|
151
|
+
permanentRedirect("/new-url");
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Auth Errors
|
|
155
|
+
|
|
156
|
+
Trigger auth-related error pages:
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
import { forbidden, unauthorized } from "next/navigation";
|
|
160
|
+
|
|
161
|
+
async function Page() {
|
|
162
|
+
const session = await getSession();
|
|
163
|
+
|
|
164
|
+
if (!session) {
|
|
165
|
+
unauthorized(); // Renders unauthorized.tsx (401)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!session.hasAccess) {
|
|
169
|
+
forbidden(); // Renders forbidden.tsx (403)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return <Dashboard />;
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Create corresponding error pages:
|
|
177
|
+
|
|
178
|
+
```tsx
|
|
179
|
+
// app/forbidden.tsx
|
|
180
|
+
export default function Forbidden() {
|
|
181
|
+
return <div>You don't have access to this resource</div>;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// app/unauthorized.tsx
|
|
185
|
+
export default function Unauthorized() {
|
|
186
|
+
return <div>Please log in to continue</div>;
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Not Found
|
|
191
|
+
|
|
192
|
+
### `not-found.tsx`
|
|
193
|
+
|
|
194
|
+
Custom 404 page for a route segment:
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
export default function NotFound() {
|
|
198
|
+
return (
|
|
199
|
+
<div>
|
|
200
|
+
<h2>Not Found</h2>
|
|
201
|
+
<p>Could not find the requested resource</p>
|
|
202
|
+
</div>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Triggering Not Found
|
|
208
|
+
|
|
209
|
+
```tsx
|
|
210
|
+
import { notFound } from "next/navigation";
|
|
211
|
+
|
|
212
|
+
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
|
|
213
|
+
const { id } = await params;
|
|
214
|
+
const post = await getPost(id);
|
|
215
|
+
|
|
216
|
+
if (!post) {
|
|
217
|
+
notFound(); // Renders closest not-found.tsx
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return <div>{post.title}</div>;
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Error Hierarchy
|
|
225
|
+
|
|
226
|
+
Errors bubble up to the nearest error boundary:
|
|
227
|
+
|
|
228
|
+
```
|
|
229
|
+
app/
|
|
230
|
+
├── error.tsx # Catches errors from all children
|
|
231
|
+
├── blog/
|
|
232
|
+
│ ├── error.tsx # Catches errors in /blog/*
|
|
233
|
+
│ └── [slug]/
|
|
234
|
+
│ ├── error.tsx # Catches errors in /blog/[slug]
|
|
235
|
+
│ └── page.tsx
|
|
236
|
+
└── layout.tsx # Errors here go to global-error.tsx
|
|
237
|
+
```
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# File Conventions
|
|
2
|
+
|
|
3
|
+
Next.js App Router uses file-based routing with special file conventions.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Project Structure](#project-structure)
|
|
8
|
+
- [Special Files](#special-files)
|
|
9
|
+
- [Route Segments](#route-segments)
|
|
10
|
+
- [Parallel Routes](#parallel-routes)
|
|
11
|
+
- [Intercepting Routes](#intercepting-routes)
|
|
12
|
+
- [Private Folders](#private-folders)
|
|
13
|
+
- [Middleware / Proxy](#middleware--proxy)
|
|
14
|
+
- [File Conventions Reference](#file-conventions-reference)
|
|
15
|
+
|
|
16
|
+
## Project Structure
|
|
17
|
+
|
|
18
|
+
Reference: https://nextjs.org/docs/app/getting-started/project-structure
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
app/
|
|
22
|
+
├── layout.tsx # Root layout (required)
|
|
23
|
+
├── page.tsx # Home page (/)
|
|
24
|
+
├── loading.tsx # Loading UI
|
|
25
|
+
├── error.tsx # Error UI
|
|
26
|
+
├── not-found.tsx # 404 UI
|
|
27
|
+
├── global-error.tsx # Global error UI
|
|
28
|
+
├── route.ts # API endpoint
|
|
29
|
+
├── template.tsx # Re-rendered layout
|
|
30
|
+
├── default.tsx # Parallel route fallback
|
|
31
|
+
├── blog/
|
|
32
|
+
│ ├── page.tsx # /blog
|
|
33
|
+
│ └── [slug]/
|
|
34
|
+
│ └── page.tsx # /blog/:slug
|
|
35
|
+
└── (group)/ # Route group (no URL impact)
|
|
36
|
+
└── page.tsx
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Special Files
|
|
40
|
+
|
|
41
|
+
| File | Purpose |
|
|
42
|
+
| --------------- | ---------------------------------------- |
|
|
43
|
+
| `page.tsx` | UI for a route segment |
|
|
44
|
+
| `layout.tsx` | Shared UI for segment and children |
|
|
45
|
+
| `loading.tsx` | Loading UI (Suspense boundary) |
|
|
46
|
+
| `error.tsx` | Error UI (Error boundary) |
|
|
47
|
+
| `not-found.tsx` | 404 UI |
|
|
48
|
+
| `route.ts` | API endpoint |
|
|
49
|
+
| `template.tsx` | Like layout but re-renders on navigation |
|
|
50
|
+
| `default.tsx` | Fallback for parallel routes |
|
|
51
|
+
|
|
52
|
+
## Route Segments
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
app/
|
|
56
|
+
├── blog/ # Static segment: /blog
|
|
57
|
+
├── [slug]/ # Dynamic segment: /:slug
|
|
58
|
+
├── [...slug]/ # Catch-all: /a/b/c
|
|
59
|
+
├── [[...slug]]/ # Optional catch-all: / or /a/b/c
|
|
60
|
+
└── (marketing)/ # Route group (ignored in URL)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Parallel Routes
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
app/
|
|
67
|
+
├── @analytics/
|
|
68
|
+
│ └── page.tsx
|
|
69
|
+
├── @sidebar/
|
|
70
|
+
│ └── page.tsx
|
|
71
|
+
└── layout.tsx # Receives { analytics, sidebar } as props
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Intercepting Routes
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
app/
|
|
78
|
+
├── feed/
|
|
79
|
+
│ └── page.tsx
|
|
80
|
+
├── @modal/
|
|
81
|
+
│ └── (.)photo/[id]/ # Intercepts /photo/[id] from /feed
|
|
82
|
+
│ └── page.tsx
|
|
83
|
+
└── photo/[id]/
|
|
84
|
+
└── page.tsx
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Conventions:
|
|
88
|
+
|
|
89
|
+
- `(.)` - same level
|
|
90
|
+
- `(..)` - one level up
|
|
91
|
+
- `(..)(..)` - two levels up
|
|
92
|
+
- `(...)` - from root
|
|
93
|
+
|
|
94
|
+
## Private Folders
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
app/
|
|
98
|
+
├── _components/ # Private folder (not a route)
|
|
99
|
+
│ └── Button.tsx
|
|
100
|
+
└── page.tsx
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Prefix with `_` to exclude from routing.
|
|
104
|
+
|
|
105
|
+
## Middleware / Proxy
|
|
106
|
+
|
|
107
|
+
### Next.js 14-15: `middleware.ts`
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
// middleware.ts (root of project)
|
|
111
|
+
import { NextResponse } from "next/server";
|
|
112
|
+
import type { NextRequest } from "next/server";
|
|
113
|
+
|
|
114
|
+
export function middleware(request: NextRequest) {
|
|
115
|
+
// Auth, redirects, rewrites, etc.
|
|
116
|
+
return NextResponse.next();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export const config = {
|
|
120
|
+
matcher: ["/dashboard/:path*", "/api/:path*"],
|
|
121
|
+
};
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Next.js 16+: `proxy.ts`
|
|
125
|
+
|
|
126
|
+
Renamed for clarity - same capabilities, different names:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
// proxy.ts (root of project)
|
|
130
|
+
import { NextResponse } from "next/server";
|
|
131
|
+
import type { NextRequest } from "next/server";
|
|
132
|
+
|
|
133
|
+
export function proxy(request: NextRequest) {
|
|
134
|
+
// Same logic as middleware
|
|
135
|
+
return NextResponse.next();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export const proxyConfig = {
|
|
139
|
+
matcher: ["/dashboard/:path*", "/api/:path*"],
|
|
140
|
+
};
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
| Version | File | Export | Config |
|
|
144
|
+
| ------- | --------------- | -------------- | ------------- |
|
|
145
|
+
| v14-15 | `middleware.ts` | `middleware()` | `config` |
|
|
146
|
+
| v16+ | `proxy.ts` | `proxy()` | `proxyConfig` |
|
|
147
|
+
|
|
148
|
+
**Migration**: Run `npx @next/codemod@latest upgrade` to auto-rename.
|
|
149
|
+
|
|
150
|
+
## File Conventions Reference
|
|
151
|
+
|
|
152
|
+
Reference: https://nextjs.org/docs/app/api-reference/file-conventions
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Font Optimization
|
|
2
|
+
|
|
3
|
+
Use `next/font` for automatic font optimization with zero layout shift.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Multiple Fonts with CSS Variables](#multiple-fonts-with-css-variables)
|
|
8
|
+
- [Local Fonts](#local-fonts)
|
|
9
|
+
- [Tailwind CSS v4 Integration](#tailwind-css-v4-integration)
|
|
10
|
+
- [Display Strategy](#display-strategy)
|
|
11
|
+
- [Common Mistakes](#common-mistakes)
|
|
12
|
+
- [Font in Specific Components](#font-in-specific-components)
|
|
13
|
+
|
|
14
|
+
## Multiple Fonts with CSS Variables
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { Inter, Roboto_Mono } from "next/font/google";
|
|
18
|
+
|
|
19
|
+
const inter = Inter({
|
|
20
|
+
subsets: ["latin"],
|
|
21
|
+
variable: "--font-inter",
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const robotoMono = Roboto_Mono({
|
|
25
|
+
subsets: ["latin"],
|
|
26
|
+
variable: "--font-roboto-mono",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
30
|
+
return (
|
|
31
|
+
<html lang="en" className={`${inter.variable} ${robotoMono.variable}`}>
|
|
32
|
+
<body>{children}</body>
|
|
33
|
+
</html>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Use in CSS:
|
|
39
|
+
|
|
40
|
+
```css
|
|
41
|
+
body {
|
|
42
|
+
font-family: var(--font-inter);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
code {
|
|
46
|
+
font-family: var(--font-roboto-mono);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Local Fonts
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import localFont from "next/font/local";
|
|
54
|
+
|
|
55
|
+
// Single file
|
|
56
|
+
const myFont = localFont({
|
|
57
|
+
src: "./fonts/MyFont.woff2",
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Multiple files for different weights
|
|
61
|
+
const myFont = localFont({
|
|
62
|
+
src: [
|
|
63
|
+
{
|
|
64
|
+
path: "./fonts/MyFont-Regular.woff2",
|
|
65
|
+
weight: "400",
|
|
66
|
+
style: "normal",
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
path: "./fonts/MyFont-Bold.woff2",
|
|
70
|
+
weight: "700",
|
|
71
|
+
style: "normal",
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Variable font with CSS variable
|
|
77
|
+
const myFont = localFont({
|
|
78
|
+
src: "./fonts/MyFont-Variable.woff2",
|
|
79
|
+
variable: "--font-my-font",
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Tailwind CSS v4 Integration
|
|
84
|
+
|
|
85
|
+
In Tailwind v4, font families are configured via CSS variables (no `tailwind.config.js` needed):
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
// app/layout.tsx
|
|
89
|
+
import { Inter } from "next/font/google";
|
|
90
|
+
|
|
91
|
+
const inter = Inter({
|
|
92
|
+
subsets: ["latin"],
|
|
93
|
+
variable: "--font-inter",
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
export default function RootLayout({ children }) {
|
|
97
|
+
return (
|
|
98
|
+
<html lang="en" className={inter.variable}>
|
|
99
|
+
<body>{children}</body>
|
|
100
|
+
</html>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```css
|
|
106
|
+
/* globals.css — Tailwind v4 */
|
|
107
|
+
@import "tailwindcss";
|
|
108
|
+
|
|
109
|
+
@theme {
|
|
110
|
+
--font-sans: var(--font-inter);
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Then use `font-sans` utility class as normal.
|
|
115
|
+
|
|
116
|
+
## Display Strategy
|
|
117
|
+
|
|
118
|
+
Control font loading behavior:
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
const inter = Inter({
|
|
122
|
+
subsets: ["latin"],
|
|
123
|
+
display: "swap", // Default - shows fallback, swaps when loaded
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Options:
|
|
127
|
+
// 'swap' - immediate fallback, swap when ready (recommended)
|
|
128
|
+
// 'block' - short block period, then swap
|
|
129
|
+
// 'fallback' - short block, short swap, then fallback
|
|
130
|
+
// 'optional' - short block, no swap (use if font is optional)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Common Mistakes
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
// Bad: Importing font in every component
|
|
137
|
+
// components/Button.tsx
|
|
138
|
+
import { Inter } from 'next/font/google'
|
|
139
|
+
const inter = Inter({ subsets: ['latin'] }) // Creates new instance each time!
|
|
140
|
+
|
|
141
|
+
// Good: Import once in layout, use CSS variable
|
|
142
|
+
// app/layout.tsx
|
|
143
|
+
const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })
|
|
144
|
+
|
|
145
|
+
// Bad: Using @import in CSS (blocks rendering)
|
|
146
|
+
/* globals.css */
|
|
147
|
+
@import url('https://fonts.googleapis.com/css2?family=Inter');
|
|
148
|
+
|
|
149
|
+
// Good: Use next/font (self-hosted, no network request)
|
|
150
|
+
import { Inter } from 'next/font/google'
|
|
151
|
+
|
|
152
|
+
// Bad: Missing subset - loads all characters
|
|
153
|
+
const inter = Inter({})
|
|
154
|
+
|
|
155
|
+
// Good: Always specify subset
|
|
156
|
+
const inter = Inter({ subsets: ['latin'] })
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Font in Specific Components
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
// For component-specific fonts, export from a shared file
|
|
163
|
+
// lib/fonts.ts
|
|
164
|
+
import { Inter, Playfair_Display } from "next/font/google";
|
|
165
|
+
|
|
166
|
+
export const inter = Inter({ subsets: ["latin"], variable: "--font-inter" });
|
|
167
|
+
export const playfair = Playfair_Display({ subsets: ["latin"], variable: "--font-playfair" });
|
|
168
|
+
|
|
169
|
+
// components/Heading.tsx
|
|
170
|
+
import { playfair } from "@/lib/fonts";
|
|
171
|
+
|
|
172
|
+
export function Heading({ children }) {
|
|
173
|
+
return <h1 className={playfair.className}>{children}</h1>;
|
|
174
|
+
}
|
|
175
|
+
```
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Functions
|
|
2
|
+
|
|
3
|
+
Next.js function APIs.
|
|
4
|
+
|
|
5
|
+
Reference: https://nextjs.org/docs/app/api-reference/functions
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Navigation Hooks (Client)](#navigation-hooks-client)
|
|
10
|
+
- [Server Functions](#server-functions)
|
|
11
|
+
- [Generate Functions](#generate-functions)
|
|
12
|
+
- [Request/Response](#requestresponse)
|
|
13
|
+
- [Common Examples](#common-examples)
|
|
14
|
+
|
|
15
|
+
## Navigation Hooks (Client)
|
|
16
|
+
|
|
17
|
+
| Hook | Purpose | Reference |
|
|
18
|
+
| --------------------------- | -------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
|
19
|
+
| `useRouter` | Programmatic navigation (`push`, `replace`, `back`, `refresh`) | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-router) |
|
|
20
|
+
| `usePathname` | Get current pathname | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-pathname) |
|
|
21
|
+
| `useSearchParams` | Read URL search parameters | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-search-params) |
|
|
22
|
+
| `useParams` | Access dynamic route parameters | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-params) |
|
|
23
|
+
| `useSelectedLayoutSegment` | Active child segment (one level) | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-selected-layout-segment) |
|
|
24
|
+
| `useSelectedLayoutSegments` | All active segments below layout | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-selected-layout-segments) |
|
|
25
|
+
| `useLinkStatus` | Check link prefetch status | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-link-status) |
|
|
26
|
+
| `useReportWebVitals` | Report Core Web Vitals metrics | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-report-web-vitals) |
|
|
27
|
+
|
|
28
|
+
## Server Functions
|
|
29
|
+
|
|
30
|
+
| Function | Purpose | Reference |
|
|
31
|
+
| ------------ | -------------------------------------------- | ---------------------------------------------------------------------- |
|
|
32
|
+
| `cookies` | Read/write cookies | [Docs](https://nextjs.org/docs/app/api-reference/functions/cookies) |
|
|
33
|
+
| `headers` | Read request headers | [Docs](https://nextjs.org/docs/app/api-reference/functions/headers) |
|
|
34
|
+
| `draftMode` | Enable preview of unpublished CMS content | [Docs](https://nextjs.org/docs/app/api-reference/functions/draft-mode) |
|
|
35
|
+
| `after` | Run code after response finishes streaming | [Docs](https://nextjs.org/docs/app/api-reference/functions/after) |
|
|
36
|
+
| `connection` | Wait for connection before dynamic rendering | [Docs](https://nextjs.org/docs/app/api-reference/functions/connection) |
|
|
37
|
+
| `userAgent` | Parse User-Agent header | [Docs](https://nextjs.org/docs/app/api-reference/functions/userAgent) |
|
|
38
|
+
|
|
39
|
+
## Generate Functions
|
|
40
|
+
|
|
41
|
+
| Function | Purpose | Reference |
|
|
42
|
+
| ----------------------- | --------------------------------------- | ----------------------------------------------------------------------------------- |
|
|
43
|
+
| `generateStaticParams` | Pre-render dynamic routes at build time | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-static-params) |
|
|
44
|
+
| `generateMetadata` | Dynamic metadata | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-metadata) |
|
|
45
|
+
| `generateViewport` | Dynamic viewport config | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-viewport) |
|
|
46
|
+
| `generateSitemaps` | Multiple sitemaps for large sites | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-sitemaps) |
|
|
47
|
+
| `generateImageMetadata` | Multiple OG images per route | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-image-metadata) |
|
|
48
|
+
|
|
49
|
+
## Request/Response
|
|
50
|
+
|
|
51
|
+
| Function | Purpose | Reference |
|
|
52
|
+
| --------------- | ------------------------------ | -------------------------------------------------------------------------- |
|
|
53
|
+
| `NextRequest` | Extended Request with helpers | [Docs](https://nextjs.org/docs/app/api-reference/functions/next-request) |
|
|
54
|
+
| `NextResponse` | Extended Response with helpers | [Docs](https://nextjs.org/docs/app/api-reference/functions/next-response) |
|
|
55
|
+
| `ImageResponse` | Generate OG images | [Docs](https://nextjs.org/docs/app/api-reference/functions/image-response) |
|
|
56
|
+
|
|
57
|
+
## Common Examples
|
|
58
|
+
|
|
59
|
+
### Navigation
|
|
60
|
+
|
|
61
|
+
Use `next/link` for internal navigation instead of `<a>` tags.
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
// Bad: Plain anchor tag
|
|
65
|
+
<a href="/about">About</a>;
|
|
66
|
+
|
|
67
|
+
// Good: Next.js Link
|
|
68
|
+
import Link from "next/link";
|
|
69
|
+
|
|
70
|
+
<Link href="/about">About</Link>;
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Active link styling:
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
"use client";
|
|
77
|
+
|
|
78
|
+
import Link from "next/link";
|
|
79
|
+
import { usePathname } from "next/navigation";
|
|
80
|
+
|
|
81
|
+
export function NavLink({ href, children }) {
|
|
82
|
+
const pathname = usePathname();
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<Link href={href} className={pathname === href ? "active" : ""}>
|
|
86
|
+
{children}
|
|
87
|
+
</Link>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Static Generation
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
// app/blog/[slug]/page.tsx
|
|
96
|
+
export async function generateStaticParams() {
|
|
97
|
+
const posts = await getPosts();
|
|
98
|
+
return posts.map((post) => ({ slug: post.slug }));
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### After Response
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
import { after } from "next/server";
|
|
106
|
+
|
|
107
|
+
export async function POST(request: Request) {
|
|
108
|
+
const data = await processRequest(request);
|
|
109
|
+
|
|
110
|
+
after(async () => {
|
|
111
|
+
await logAnalytics(data);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return Response.json({ success: true });
|
|
115
|
+
}
|
|
116
|
+
```
|