tribunal-kit 3.0.0 → 3.1.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/.agent/ARCHITECTURE.md +99 -99
- package/.agent/GEMINI.md +52 -52
- package/.agent/agents/accessibility-reviewer.md +187 -220
- package/.agent/agents/ai-code-reviewer.md +199 -233
- package/.agent/agents/backend-specialist.md +215 -238
- package/.agent/agents/code-archaeologist.md +161 -181
- package/.agent/agents/database-architect.md +184 -207
- package/.agent/agents/debugger.md +191 -218
- package/.agent/agents/dependency-reviewer.md +103 -136
- package/.agent/agents/devops-engineer.md +218 -238
- package/.agent/agents/documentation-writer.md +201 -221
- package/.agent/agents/explorer-agent.md +160 -180
- package/.agent/agents/frontend-reviewer.md +160 -194
- package/.agent/agents/frontend-specialist.md +248 -237
- package/.agent/agents/game-developer.md +48 -52
- package/.agent/agents/logic-reviewer.md +116 -149
- package/.agent/agents/mobile-developer.md +200 -223
- package/.agent/agents/mobile-reviewer.md +162 -195
- package/.agent/agents/orchestrator.md +181 -211
- package/.agent/agents/penetration-tester.md +157 -174
- package/.agent/agents/performance-optimizer.md +183 -203
- package/.agent/agents/performance-reviewer.md +178 -211
- package/.agent/agents/product-manager.md +142 -162
- package/.agent/agents/product-owner.md +6 -25
- package/.agent/agents/project-planner.md +142 -162
- package/.agent/agents/qa-automation-engineer.md +225 -242
- package/.agent/agents/security-auditor.md +174 -194
- package/.agent/agents/seo-specialist.md +193 -213
- package/.agent/agents/sql-reviewer.md +161 -194
- package/.agent/agents/supervisor-agent.md +184 -203
- package/.agent/agents/swarm-worker-contracts.md +17 -17
- package/.agent/agents/swarm-worker-registry.md +46 -46
- package/.agent/agents/test-coverage-reviewer.md +160 -193
- package/.agent/agents/test-engineer.md +0 -21
- package/.agent/agents/type-safety-reviewer.md +175 -208
- package/.agent/patterns/generator.md +9 -9
- package/.agent/patterns/inversion.md +12 -12
- package/.agent/patterns/pipeline.md +9 -9
- package/.agent/patterns/reviewer.md +13 -13
- package/.agent/patterns/tool-wrapper.md +9 -9
- package/.agent/rules/GEMINI.md +63 -63
- package/.agent/scripts/compress_skills.py +167 -0
- package/.agent/scripts/consolidate_skills.py +173 -0
- package/.agent/scripts/deep_compress.py +202 -0
- package/.agent/scripts/minify_context.py +80 -0
- package/.agent/scripts/security_scan.py +1 -1
- package/.agent/scripts/strip_tribunal.py +41 -0
- package/.agent/skills/agent-organizer/SKILL.md +92 -126
- package/.agent/skills/agentic-patterns/SKILL.md +0 -70
- package/.agent/skills/ai-prompt-injection-defense/SKILL.md +126 -160
- package/.agent/skills/api-patterns/SKILL.md +123 -215
- package/.agent/skills/api-security-auditor/SKILL.md +143 -177
- package/.agent/skills/app-builder/SKILL.md +326 -50
- package/.agent/skills/app-builder/templates/SKILL.md +13 -15
- package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +16 -16
- package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +22 -22
- package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +18 -18
- package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +20 -20
- package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +17 -17
- package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +18 -18
- package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +21 -21
- package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +19 -19
- package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +26 -26
- package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +26 -26
- package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +19 -19
- package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +18 -18
- package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +20 -20
- package/.agent/skills/appflow-wireframe/SKILL.md +87 -121
- package/.agent/skills/architecture/SKILL.md +82 -252
- package/.agent/skills/authentication-best-practices/SKILL.md +139 -173
- package/.agent/skills/bash-linux/SKILL.md +120 -154
- package/.agent/skills/behavioral-modes/SKILL.md +8 -69
- package/.agent/skills/brainstorming/SKILL.md +428 -104
- package/.agent/skills/building-native-ui/SKILL.md +143 -174
- package/.agent/skills/clean-code/SKILL.md +323 -360
- package/.agent/skills/code-review-checklist/SKILL.md +0 -62
- package/.agent/skills/config-validator/SKILL.md +107 -141
- package/.agent/skills/csharp-developer/SKILL.md +468 -528
- package/.agent/skills/database-design/SKILL.md +104 -369
- package/.agent/skills/deployment-procedures/SKILL.md +111 -145
- package/.agent/skills/devops-engineer/SKILL.md +295 -332
- package/.agent/skills/devops-incident-responder/SKILL.md +79 -113
- package/.agent/skills/doc.md +5 -5
- package/.agent/skills/documentation-templates/SKILL.md +19 -63
- package/.agent/skills/edge-computing/SKILL.md +123 -157
- package/.agent/skills/extract-design-system/SKILL.md +100 -134
- package/.agent/skills/framer-motion-expert/SKILL.md +111 -855
- package/.agent/skills/frontend-design/SKILL.md +151 -499
- package/.agent/skills/game-design-expert/SKILL.md +71 -105
- package/.agent/skills/game-engineering-expert/SKILL.md +88 -122
- package/.agent/skills/geo-fundamentals/SKILL.md +89 -124
- package/.agent/skills/github-operations/SKILL.md +279 -314
- package/.agent/skills/gsap-expert/SKILL.md +119 -826
- package/.agent/skills/i18n-localization/SKILL.md +104 -138
- package/.agent/skills/intelligent-routing/SKILL.md +159 -127
- package/.agent/skills/lint-and-validate/SKILL.md +8 -52
- package/.agent/skills/llm-engineering/SKILL.md +344 -357
- package/.agent/skills/local-first/SKILL.md +120 -154
- package/.agent/skills/mcp-builder/SKILL.md +84 -118
- package/.agent/skills/mobile-design/SKILL.md +213 -219
- package/.agent/skills/motion-engineering/SKILL.md +184 -0
- package/.agent/skills/nextjs-react-expert/SKILL.md +99 -698
- package/.agent/skills/nodejs-best-practices/SKILL.md +498 -559
- package/.agent/skills/observability/SKILL.md +293 -330
- package/.agent/skills/parallel-agents/SKILL.md +88 -122
- package/.agent/skills/performance-profiling/SKILL.md +217 -254
- package/.agent/skills/plan-writing/SKILL.md +84 -118
- package/.agent/skills/platform-engineer/SKILL.md +89 -123
- package/.agent/skills/playwright-best-practices/SKILL.md +128 -162
- package/.agent/skills/powershell-windows/SKILL.md +112 -146
- package/.agent/skills/python-patterns/SKILL.md +7 -35
- package/.agent/skills/python-pro/SKILL.md +148 -754
- package/.agent/skills/react-specialist/SKILL.md +123 -827
- package/.agent/skills/readme-builder/SKILL.md +15 -85
- package/.agent/skills/realtime-patterns/SKILL.md +269 -304
- package/.agent/skills/red-team-tactics/SKILL.md +10 -51
- package/.agent/skills/rust-pro/SKILL.md +623 -701
- package/.agent/skills/seo-fundamentals/SKILL.md +120 -154
- package/.agent/skills/server-management/SKILL.md +156 -190
- package/.agent/skills/shadcn-ui-expert/SKILL.md +172 -206
- package/.agent/skills/skill-creator/SKILL.md +18 -58
- package/.agent/skills/sql-pro/SKILL.md +579 -633
- package/.agent/skills/supabase-postgres-best-practices/SKILL.md +28 -68
- package/.agent/skills/swiftui-expert/SKILL.md +142 -176
- package/.agent/skills/systematic-debugging/SKILL.md +84 -118
- package/.agent/skills/tailwind-patterns/SKILL.md +516 -576
- package/.agent/skills/tdd-workflow/SKILL.md +103 -137
- package/.agent/skills/test-result-analyzer/SKILL.md +33 -73
- package/.agent/skills/testing-patterns/SKILL.md +512 -573
- package/.agent/skills/trend-researcher/SKILL.md +30 -71
- package/.agent/skills/ui-ux-pro-max/SKILL.md +0 -41
- package/.agent/skills/ui-ux-researcher/SKILL.md +51 -91
- package/.agent/skills/vue-expert/SKILL.md +127 -866
- package/.agent/skills/vulnerability-scanner/SKILL.md +354 -269
- package/.agent/skills/web-accessibility-auditor/SKILL.md +159 -193
- package/.agent/skills/web-design-guidelines/SKILL.md +17 -61
- package/.agent/skills/webapp-testing/SKILL.md +111 -145
- package/.agent/skills/whimsy-injector/SKILL.md +58 -132
- package/.agent/skills/workflow-optimizer/SKILL.md +28 -68
- package/.agent/workflows/api-tester.md +151 -151
- package/.agent/workflows/audit.md +127 -138
- package/.agent/workflows/brainstorm.md +110 -110
- package/.agent/workflows/changelog.md +112 -112
- package/.agent/workflows/create.md +124 -124
- package/.agent/workflows/debug.md +165 -189
- package/.agent/workflows/deploy.md +180 -189
- package/.agent/workflows/enhance.md +128 -151
- package/.agent/workflows/fix.md +114 -135
- package/.agent/workflows/generate.md +12 -4
- package/.agent/workflows/migrate.md +160 -160
- package/.agent/workflows/orchestrate.md +168 -168
- package/.agent/workflows/performance-benchmarker.md +114 -123
- package/.agent/workflows/plan.md +173 -173
- package/.agent/workflows/preview.md +80 -80
- package/.agent/workflows/refactor.md +161 -183
- package/.agent/workflows/review-ai.md +101 -129
- package/.agent/workflows/review.md +116 -116
- package/.agent/workflows/session.md +94 -94
- package/.agent/workflows/status.md +79 -79
- package/.agent/workflows/strengthen-skills.md +138 -139
- package/.agent/workflows/swarm.md +179 -179
- package/.agent/workflows/test.md +189 -211
- package/.agent/workflows/tribunal-backend.md +93 -113
- package/.agent/workflows/tribunal-database.md +94 -115
- package/.agent/workflows/tribunal-frontend.md +95 -118
- package/.agent/workflows/tribunal-full.md +92 -133
- package/.agent/workflows/tribunal-mobile.md +94 -119
- package/.agent/workflows/tribunal-performance.md +109 -133
- package/.agent/workflows/ui-ux-pro-max.md +122 -143
- package/package.json +1 -1
- package/.agent/skills/api-patterns/api-style.md +0 -42
- package/.agent/skills/api-patterns/auth.md +0 -24
- package/.agent/skills/api-patterns/documentation.md +0 -26
- package/.agent/skills/api-patterns/graphql.md +0 -41
- package/.agent/skills/api-patterns/rate-limiting.md +0 -31
- package/.agent/skills/api-patterns/response.md +0 -37
- package/.agent/skills/api-patterns/rest.md +0 -40
- package/.agent/skills/api-patterns/security-testing.md +0 -122
- package/.agent/skills/api-patterns/trpc.md +0 -41
- package/.agent/skills/api-patterns/versioning.md +0 -22
- package/.agent/skills/app-builder/agent-coordination.md +0 -71
- package/.agent/skills/app-builder/feature-building.md +0 -53
- package/.agent/skills/app-builder/project-detection.md +0 -34
- package/.agent/skills/app-builder/scaffolding.md +0 -118
- package/.agent/skills/app-builder/tech-stack.md +0 -40
- package/.agent/skills/architecture/context-discovery.md +0 -43
- package/.agent/skills/architecture/examples.md +0 -94
- package/.agent/skills/architecture/pattern-selection.md +0 -68
- package/.agent/skills/architecture/patterns-reference.md +0 -50
- package/.agent/skills/architecture/trade-off-analysis.md +0 -77
- package/.agent/skills/brainstorming/dynamic-questioning.md +0 -360
- package/.agent/skills/database-design/database-selection.md +0 -43
- package/.agent/skills/database-design/indexing.md +0 -39
- package/.agent/skills/database-design/migrations.md +0 -48
- package/.agent/skills/database-design/optimization.md +0 -36
- package/.agent/skills/database-design/orm-selection.md +0 -30
- package/.agent/skills/database-design/schema-design.md +0 -56
- package/.agent/skills/frontend-design/animation-guide.md +0 -331
- package/.agent/skills/frontend-design/color-system.md +0 -329
- package/.agent/skills/frontend-design/decision-trees.md +0 -418
- package/.agent/skills/frontend-design/motion-graphics.md +0 -306
- package/.agent/skills/frontend-design/typography-system.md +0 -363
- package/.agent/skills/frontend-design/ux-psychology.md +0 -1116
- package/.agent/skills/frontend-design/visual-effects.md +0 -383
- package/.agent/skills/intelligent-routing/router-manifest.md +0 -65
- package/.agent/skills/mobile-design/decision-trees.md +0 -516
- package/.agent/skills/mobile-design/mobile-backend.md +0 -491
- package/.agent/skills/mobile-design/mobile-color-system.md +0 -420
- package/.agent/skills/mobile-design/mobile-debugging.md +0 -122
- package/.agent/skills/mobile-design/mobile-design-thinking.md +0 -357
- package/.agent/skills/mobile-design/mobile-navigation.md +0 -458
- package/.agent/skills/mobile-design/mobile-performance.md +0 -767
- package/.agent/skills/mobile-design/mobile-testing.md +0 -356
- package/.agent/skills/mobile-design/mobile-typography.md +0 -433
- package/.agent/skills/mobile-design/platform-android.md +0 -666
- package/.agent/skills/mobile-design/platform-ios.md +0 -561
- package/.agent/skills/mobile-design/touch-psychology.md +0 -537
- package/.agent/skills/nextjs-react-expert/1-async-eliminating-waterfalls.md +0 -312
- package/.agent/skills/nextjs-react-expert/2-bundle-bundle-size-optimization.md +0 -240
- package/.agent/skills/nextjs-react-expert/3-server-server-side-performance.md +0 -490
- package/.agent/skills/nextjs-react-expert/4-client-client-side-data-fetching.md +0 -264
- package/.agent/skills/nextjs-react-expert/5-rerender-re-render-optimization.md +0 -581
- package/.agent/skills/nextjs-react-expert/6-rendering-rendering-performance.md +0 -432
- package/.agent/skills/nextjs-react-expert/7-js-javascript-performance.md +0 -684
- package/.agent/skills/nextjs-react-expert/8-advanced-advanced-patterns.md +0 -150
- package/.agent/skills/vulnerability-scanner/checklists.md +0 -121
|
@@ -1,783 +1,184 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: nextjs-react-expert
|
|
3
|
-
description: Next.js 15+ App Router mastery. Server Components, Server Actions,
|
|
3
|
+
description: Next.js 15+ App Router mastery. Server Components, Server Actions, PPR, caching, metadata, middleware, parallel/intercepting routes. Use when building Next.js apps or optimizing Next.js performance.
|
|
4
4
|
allowed-tools: Read, Write, Edit, Glob, Grep
|
|
5
|
-
version:
|
|
6
|
-
last-updated: 2026-
|
|
7
|
-
applies-to-model: gemini-
|
|
5
|
+
version: 3.1.0
|
|
6
|
+
last-updated: 2026-04-06
|
|
7
|
+
applies-to-model: gemini-3-1-pro, claude-3-7-sonnet
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
# Next.js 15+ App Router —
|
|
10
|
+
# Next.js 15+ App Router — Dense Reference
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
## Hallucination Traps (Read First)
|
|
13
|
+
- ❌ `pages/api/` or `_app.tsx` → ✅ App Router only: `app/api/route.ts`, `app/layout.tsx`
|
|
14
|
+
- ❌ `getServerSideProps` → ✅ `async function Page()` fetches directly
|
|
15
|
+
- ❌ `next/router` → ✅ `next/navigation` (`useRouter`, `usePathname`, `useSearchParams`)
|
|
16
|
+
- ❌ Server Action without `"use server"` → ✅ Required at top of file or top of function
|
|
17
|
+
- ❌ Fetch is cached by default → ✅ **Next.js 15 changed this**: `fetch()` is UNCACHED by default
|
|
18
|
+
- ❌ `cookies()` at top of page → ✅ Opts entire route into dynamic rendering, breaking PPR. Wrap inside `<Suspense>`
|
|
19
|
+
- ❌ Passing functions as props Server → Client → ✅ Illegal. Use Server Actions instead.
|
|
20
|
+
- ❌ Plain `Response` in route handler → ✅ Use `NextResponse.json()`
|
|
15
21
|
|
|
16
22
|
---
|
|
17
23
|
|
|
18
|
-
##
|
|
24
|
+
## App Router Conventions
|
|
19
25
|
|
|
20
|
-
|
|
21
|
-
|---|---|
|
|
22
|
-
| `getServerSideProps` / `getStaticProps` | **Server Components** fetch directly |
|
|
23
|
-
| Manual `useMemo()` / `useCallback()` | **React Compiler** handles memoization |
|
|
24
|
-
| Client-side form handling | **Server Actions** native mutations |
|
|
25
|
-
| Loading spinners on client | **Streaming UI** + `<Suspense>` boundaries |
|
|
26
|
-
| Static *or* Dynamic pages | **Partial Prerendering (PPR)** — both in one route |
|
|
27
|
-
| `next/router` (`useRouter`) | `next/navigation` (`useRouter`, `usePathname`, `useSearchParams`) |
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## App Router File Conventions
|
|
32
|
-
|
|
33
|
-
```
|
|
26
|
+
```text
|
|
34
27
|
app/
|
|
35
|
-
├── layout.tsx
|
|
36
|
-
├── page.tsx
|
|
37
|
-
├── loading.tsx
|
|
38
|
-
├── error.tsx
|
|
39
|
-
├── not-found.tsx
|
|
40
|
-
├── global-error.tsx
|
|
41
|
-
|
|
42
|
-
├──
|
|
43
|
-
|
|
44
|
-
│ ├── page.tsx ← /dashboard
|
|
45
|
-
│ ├── loading.tsx ← Dashboard loading state
|
|
46
|
-
│ ├── error.tsx ← Dashboard error boundary
|
|
47
|
-
│ │
|
|
48
|
-
│ ├── settings/
|
|
49
|
-
│ │ └── page.tsx ← /dashboard/settings
|
|
50
|
-
│ │
|
|
51
|
-
│ └── [userId]/ ← Dynamic segment
|
|
52
|
-
│ └── page.tsx ← /dashboard/abc123
|
|
53
|
-
│
|
|
54
|
-
├── api/
|
|
55
|
-
│ └── users/
|
|
56
|
-
│ └── route.ts ← API Route Handler (GET, POST, etc.)
|
|
57
|
-
│
|
|
58
|
-
├── @modal/ ← Parallel route (named slot)
|
|
59
|
-
│ └── login/
|
|
60
|
-
│ └── page.tsx ← Rendered in parallel with main content
|
|
61
|
-
│
|
|
62
|
-
└── (marketing)/ ← Route group (no URL impact — for layout organization)
|
|
63
|
-
├── layout.tsx ← Layout ONLY for marketing pages
|
|
64
|
-
├── about/
|
|
65
|
-
│ └── page.tsx ← /about (NOT /marketing/about)
|
|
66
|
-
└── pricing/
|
|
67
|
-
└── page.tsx ← /pricing
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
```tsx
|
|
71
|
-
// ❌ HALLUCINATION TRAP: These Pages Router files DO NOT EXIST in App Router
|
|
72
|
-
// _app.tsx → use app/layout.tsx
|
|
73
|
-
// _document.tsx → use app/layout.tsx with <html> and <body>
|
|
74
|
-
// pages/api/ → use app/api/route.ts
|
|
75
|
-
// getServerSideProps → fetch directly in Server Components
|
|
76
|
-
// getStaticProps → fetch at build time or use generateStaticParams
|
|
28
|
+
├── layout.tsx ← Root shell (HTML/BODY)
|
|
29
|
+
├── page.tsx ← Route UI
|
|
30
|
+
├── loading.tsx ← Auto-suspense fallback
|
|
31
|
+
├── error.tsx ← Error boundary (Must be "use client")
|
|
32
|
+
├── not-found.tsx ← 404 UI
|
|
33
|
+
├── global-error.tsx ← Root error boundary
|
|
34
|
+
├── api/users/route.ts ← API Handler (GET, POST)
|
|
35
|
+
├── @modal/login/page.tsx ← Parallel Route (renders in same layout)
|
|
36
|
+
└── (auth)/login/page.tsx ← Route Group (doesn't affect URL)
|
|
77
37
|
```
|
|
78
38
|
|
|
79
39
|
---
|
|
80
40
|
|
|
81
41
|
## Server vs Client Components
|
|
82
42
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
```
|
|
86
|
-
Default: Server Component (Zero JS sent to client)
|
|
87
|
-
Switch to Client ('use client') ONLY when:
|
|
88
|
-
✓ Uses browser APIs (window, localStorage, navigator, IntersectionObserver)
|
|
89
|
-
✓ Needs DOM event handlers (onClick, onChange, onSubmit)
|
|
90
|
-
✓ Needs state/effects (useState, useEffect, useRef for DOM)
|
|
91
|
-
✓ Needs Framer Motion, GSAP, or client-side animation libraries
|
|
92
|
-
|
|
93
|
-
Everything else stays on the server.
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### The Interleaving Pattern
|
|
43
|
+
- **Server Components (Default)**: Zero JS. Direct DB access. Secure env vars.
|
|
44
|
+
- **Client Components (`"use client"`)**: Lifecycle (`useEffect`), State (`useState`), Browser APIs (`window`), Event listeners (`onClick`).
|
|
97
45
|
|
|
98
46
|
```tsx
|
|
99
|
-
// ✅
|
|
100
|
-
|
|
101
|
-
import { ClientSidebar } from "./ClientSidebar";
|
|
102
|
-
import { ServerStats } from "./ServerStats"; // fetches DB directly
|
|
103
|
-
|
|
104
|
-
export default async function DashboardPage() {
|
|
105
|
-
const stats = await getStats(); // no API call needed — direct DB
|
|
106
|
-
|
|
47
|
+
// ✅ INTERLEAVING PATTERN: Pass Server Component as children to Client Component
|
|
48
|
+
export default function Page() {
|
|
107
49
|
return (
|
|
108
|
-
<
|
|
109
|
-
<
|
|
110
|
-
|
|
111
|
-
</ClientSidebar>
|
|
112
|
-
</div>
|
|
50
|
+
<ClientSidebar> {/* "use client" */}
|
|
51
|
+
<ServerStats /> {/* Server: zero JS bundle, fetches DB */}
|
|
52
|
+
</ClientSidebar>
|
|
113
53
|
);
|
|
114
54
|
}
|
|
115
|
-
|
|
116
|
-
// ❌ WRONG: Making the entire page "use client" because one button needs onClick
|
|
117
|
-
// This ships ALL the JS to the client — defeats the purpose of RSC
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### Serialization Boundary
|
|
121
|
-
|
|
122
|
-
```tsx
|
|
123
|
-
// Only serializable data can cross the server→client boundary
|
|
124
|
-
// ✅ Can pass: strings, numbers, booleans, arrays, plain objects, Date, Map, Set
|
|
125
|
-
// ❌ Cannot pass: functions, class instances, DOM nodes, Symbols
|
|
126
|
-
|
|
127
|
-
// ❌ BAD: Passing a function from server to client
|
|
128
|
-
<ClientButton onClick={() => deleteItem(id)} /> // functions aren't serializable
|
|
129
|
-
|
|
130
|
-
// ✅ GOOD: Use a Server Action instead
|
|
131
|
-
<ClientButton deleteAction={deleteItemAction} itemId={id} />
|
|
132
|
-
|
|
133
|
-
// In ClientButton:
|
|
134
|
-
"use client";
|
|
135
|
-
function ClientButton({ deleteAction, itemId }) {
|
|
136
|
-
return <button onClick={() => deleteAction(itemId)}>Delete</button>;
|
|
137
|
-
}
|
|
138
55
|
```
|
|
139
56
|
|
|
140
57
|
---
|
|
141
58
|
|
|
142
|
-
## Server Actions
|
|
59
|
+
## Server Actions (Mutations)
|
|
143
60
|
|
|
144
61
|
```tsx
|
|
145
|
-
|
|
146
|
-
// They can be called from client or server components
|
|
147
|
-
|
|
148
|
-
// ✅ Inline Server Action (defined in a Server Component)
|
|
149
|
-
export default function Page() {
|
|
150
|
-
async function createUser(formData: FormData) {
|
|
151
|
-
"use server"; // marks this function as a Server Action
|
|
152
|
-
|
|
153
|
-
const name = formData.get("name") as string;
|
|
154
|
-
const email = formData.get("email") as string;
|
|
155
|
-
|
|
156
|
-
await db.user.create({ data: { name, email } });
|
|
157
|
-
revalidatePath("/users"); // bust cache for /users
|
|
158
|
-
redirect("/users"); // redirect after mutation
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return (
|
|
162
|
-
<form action={createUser}>
|
|
163
|
-
<input name="name" required />
|
|
164
|
-
<input name="email" type="email" required />
|
|
165
|
-
<button type="submit">Create</button>
|
|
166
|
-
</form>
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### Separate Action File
|
|
172
|
-
|
|
173
|
-
```tsx
|
|
174
|
-
// app/actions/user.ts
|
|
175
|
-
"use server";
|
|
176
|
-
|
|
62
|
+
"use server"
|
|
177
63
|
import { revalidatePath } from "next/cache";
|
|
178
|
-
import { redirect } from "next/navigation";
|
|
179
64
|
import { z } from "zod";
|
|
180
65
|
|
|
181
|
-
const
|
|
182
|
-
name: z.string().min(2),
|
|
183
|
-
email: z.string().email(),
|
|
184
|
-
});
|
|
66
|
+
const Schema = z.object({ name: z.string().min(2) });
|
|
185
67
|
|
|
186
68
|
export async function createUser(prevState: any, formData: FormData) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
try {
|
|
197
|
-
await db.user.create({ data: parsed.data });
|
|
198
|
-
} catch (e) {
|
|
199
|
-
return { errors: { _form: ["Failed to create user"] } };
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
revalidatePath("/users");
|
|
203
|
-
redirect("/users");
|
|
69
|
+
// ❌ TRAP: ALWAYS validate formData. Never trust client input.
|
|
70
|
+
const parsed = Schema.safeParse({ name: formData.get("name") });
|
|
71
|
+
if (!parsed.success) return { errors: parsed.error.flatten().fieldErrors };
|
|
72
|
+
|
|
73
|
+
await db.user.create({ data: parsed.data });
|
|
74
|
+
revalidatePath("/users"); // Clears cache so next render shows new user
|
|
75
|
+
return { success: true };
|
|
204
76
|
}
|
|
205
|
-
|
|
206
|
-
// ❌ HALLUCINATION TRAP: Server Actions MUST be in a file with "use server"
|
|
207
|
-
// at the top, OR defined inline with "use server" inside the function body.
|
|
208
|
-
// They CANNOT be imported from a regular module.
|
|
209
|
-
|
|
210
|
-
// ❌ HALLUCINATION TRAP: Always validate input in Server Actions.
|
|
211
|
-
// FormData comes from the client — it's user input. Never trust it.
|
|
212
77
|
```
|
|
213
78
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
## Data Fetching & Caching
|
|
217
|
-
|
|
218
|
-
### Fetching in Server Components
|
|
219
|
-
|
|
79
|
+
Client usage (React 19):
|
|
220
80
|
```tsx
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
81
|
+
"use client"
|
|
82
|
+
import { useActionState } from "react";
|
|
83
|
+
import { createUser } from "./actions";
|
|
84
|
+
|
|
85
|
+
export function UserForm() {
|
|
86
|
+
const [state, formAction, isPending] = useActionState(createUser, null);
|
|
87
|
+
return (
|
|
88
|
+
<form action={formAction}>
|
|
89
|
+
<input name="name" />
|
|
90
|
+
<button disabled={isPending}>Submit</button>
|
|
91
|
+
{state?.errors?.name && <p>{state.errors.name}</p>}
|
|
92
|
+
</form>
|
|
93
|
+
)
|
|
231
94
|
}
|
|
232
95
|
```
|
|
233
96
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
```tsx
|
|
237
|
-
// 1. Static (cached forever until revalidated)
|
|
238
|
-
const data = await fetch(url); // default: cached
|
|
239
|
-
|
|
240
|
-
// 2. ISR (Incremental Static Regeneration)
|
|
241
|
-
const data = await fetch(url, {
|
|
242
|
-
next: { revalidate: 60 }, // revalidate every 60 seconds
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
// 3. Dynamic (never cached)
|
|
246
|
-
const data = await fetch(url, { cache: "no-store" });
|
|
247
|
-
|
|
248
|
-
// 4. On-demand revalidation (Server Actions / Webhooks)
|
|
249
|
-
import { revalidatePath, revalidateTag } from "next/cache";
|
|
250
|
-
|
|
251
|
-
// Revalidate a specific path
|
|
252
|
-
revalidatePath("/dashboard");
|
|
253
|
-
|
|
254
|
-
// Revalidate by tag
|
|
255
|
-
const data = await fetch(url, { next: { tags: ["users"] } });
|
|
256
|
-
// Later, in a Server Action:
|
|
257
|
-
revalidateTag("users"); // busts all fetches tagged "users"
|
|
258
|
-
|
|
259
|
-
// ❌ HALLUCINATION TRAP: Next.js 15 changed the default caching behavior
|
|
260
|
-
// In Next.js 14: fetch was cached by default
|
|
261
|
-
// In Next.js 15: fetch is NOT cached by default (dynamic by default)
|
|
262
|
-
// You must explicitly opt into caching with next.revalidate or cache: "force-cache"
|
|
263
|
-
```
|
|
97
|
+
---
|
|
264
98
|
|
|
265
|
-
|
|
99
|
+
## Data Fetching & Caching (Next.js 15)
|
|
266
100
|
|
|
267
101
|
```tsx
|
|
268
|
-
|
|
102
|
+
// Next.js 15 caching defaults
|
|
103
|
+
const dynamic = await fetch(url); // 15 default: NO CACHE
|
|
104
|
+
const static = await fetch(url, { cache: "force-cache" }); // Static
|
|
105
|
+
const isr = await fetch(url, { next: { revalidate: 3600 } }); // Revalidate every hour
|
|
106
|
+
const tagged = await fetch(url, { next: { tags: ["user-1"] } }); // On-demand via revalidateTag()
|
|
269
107
|
|
|
270
|
-
//
|
|
108
|
+
// DB calls without fetch
|
|
109
|
+
import { unstable_cache } from "next/cache";
|
|
271
110
|
const getCachedUser = unstable_cache(
|
|
272
|
-
async (
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
["user-by-id"], // cache key parts
|
|
276
|
-
{
|
|
277
|
-
revalidate: 3600, // 1 hour
|
|
278
|
-
tags: ["user"], // for on-demand revalidation
|
|
279
|
-
}
|
|
111
|
+
async (id) => db.user.findUnique({ where: { id } }),
|
|
112
|
+
["user-cache-key"],
|
|
113
|
+
{ revalidate: 60, tags: ["users"] }
|
|
280
114
|
);
|
|
281
|
-
|
|
282
|
-
// Usage:
|
|
283
|
-
const user = await getCachedUser("abc123");
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
---
|
|
287
|
-
|
|
288
|
-
## Waterfall Elimination
|
|
289
|
-
|
|
290
|
-
### The Problem
|
|
291
|
-
|
|
292
|
-
```tsx
|
|
293
|
-
// ❌ CRITICAL WATERFALL: Each await blocks the next
|
|
294
|
-
async function Dashboard() {
|
|
295
|
-
const user = await getUser(); // 200ms
|
|
296
|
-
const posts = await getPosts(); // 200ms (waits for user)
|
|
297
|
-
const analytics = await getAnalytics(); // 200ms (waits for posts)
|
|
298
|
-
// Total: 600ms sequential
|
|
299
|
-
}
|
|
300
115
|
```
|
|
301
116
|
|
|
302
|
-
###
|
|
303
|
-
|
|
117
|
+
### Waterfall Elimination
|
|
304
118
|
```tsx
|
|
305
|
-
// ✅
|
|
306
|
-
|
|
307
|
-
const [user, posts, analytics] = await Promise.all([
|
|
308
|
-
getUser(),
|
|
309
|
-
getPosts(),
|
|
310
|
-
getAnalytics(),
|
|
311
|
-
]);
|
|
312
|
-
// Total: ~200ms (time of slowest call)
|
|
313
|
-
}
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
### Fix 2: Streaming with Suspense (Pro-Max)
|
|
119
|
+
// ✅ Parallel Fetching:
|
|
120
|
+
const [user, posts] = await Promise.all([getUser(), getPosts()]);
|
|
317
121
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
export default function Dashboard() {
|
|
122
|
+
// ✅ Streaming (PPR-compatible):
|
|
123
|
+
export default function Page() {
|
|
321
124
|
return (
|
|
322
125
|
<main>
|
|
323
|
-
<
|
|
324
|
-
|
|
325
|
-
<Suspense fallback={<
|
|
326
|
-
<
|
|
327
|
-
</Suspense>
|
|
328
|
-
|
|
329
|
-
<Suspense fallback={<ChartSkeleton />}>
|
|
330
|
-
<VerySlowChart /> {/* streams when API resolves */}
|
|
126
|
+
<FastNav />
|
|
127
|
+
{/* Page shell loads instantly, SlowChart streams in when ready */}
|
|
128
|
+
<Suspense fallback={<Skeleton />}>
|
|
129
|
+
<SlowChart />
|
|
331
130
|
</Suspense>
|
|
332
131
|
</main>
|
|
333
132
|
);
|
|
334
133
|
}
|
|
335
|
-
|
|
336
|
-
// Each Suspense boundary independently streams its content
|
|
337
|
-
// The user sees the page progressively — not a blank screen
|
|
338
134
|
```
|
|
339
135
|
|
|
340
136
|
---
|
|
341
137
|
|
|
342
138
|
## Partial Prerendering (PPR)
|
|
343
139
|
|
|
140
|
+
PPR static-generates the route shell and streams dynamic parts.
|
|
344
141
|
```tsx
|
|
345
142
|
// next.config.ts
|
|
346
|
-
export default {
|
|
347
|
-
experimental: {
|
|
348
|
-
ppr: true, // Enable Partial Prerendering
|
|
349
|
-
},
|
|
350
|
-
};
|
|
143
|
+
export default { experimental: { ppr: true } };
|
|
351
144
|
|
|
352
|
-
//
|
|
353
|
-
|
|
354
|
-
// Dynamic parts stream in from the server
|
|
145
|
+
// Any component reading cookies/headers inside a Suspense boundary becomes a dynamic hole
|
|
146
|
+
import { cookies } from "next/headers";
|
|
355
147
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
<Header />
|
|
361
|
-
<ProductDetails id={params.id} />
|
|
148
|
+
async function Cart() {
|
|
149
|
+
const c = await cookies(); // Next.js 15 cookies are async!
|
|
150
|
+
const cartId = c.get("cartId");
|
|
151
|
+
}
|
|
362
152
|
|
|
363
|
-
|
|
153
|
+
export default function Page() {
|
|
154
|
+
return (
|
|
155
|
+
<div>
|
|
156
|
+
<StaticHeader /> {/* Cached at build time on CDN */}
|
|
364
157
|
<Suspense fallback={<CartSkeleton />}>
|
|
365
|
-
<
|
|
158
|
+
<Cart /> {/* Dynamic, streamed at request time */}
|
|
366
159
|
</Suspense>
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
<LiveReviews /> {/* real-time data — dynamic */}
|
|
370
|
-
</Suspense>
|
|
371
|
-
</main>
|
|
372
|
-
);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// ❌ HALLUCINATION TRAP: Using cookies(), headers(), or searchParams at the
|
|
376
|
-
// top level of a component tree forces the ENTIRE route to be dynamic.
|
|
377
|
-
// Isolate dynamic data inside Suspense boundaries for PPR to work.
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
---
|
|
381
|
-
|
|
382
|
-
## Metadata & SEO
|
|
383
|
-
|
|
384
|
-
### `generateMetadata` (Dynamic)
|
|
385
|
-
|
|
386
|
-
```tsx
|
|
387
|
-
import { Metadata } from "next";
|
|
388
|
-
|
|
389
|
-
// Static metadata
|
|
390
|
-
export const metadata: Metadata = {
|
|
391
|
-
title: "My App",
|
|
392
|
-
description: "The best app ever",
|
|
393
|
-
openGraph: { title: "My App", description: "...", images: ["/og.png"] },
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
// Dynamic metadata (based on params/data)
|
|
397
|
-
export async function generateMetadata({
|
|
398
|
-
params,
|
|
399
|
-
}: {
|
|
400
|
-
params: { slug: string };
|
|
401
|
-
}): Promise<Metadata> {
|
|
402
|
-
const post = await getPost(params.slug);
|
|
403
|
-
|
|
404
|
-
return {
|
|
405
|
-
title: post.title,
|
|
406
|
-
description: post.excerpt,
|
|
407
|
-
openGraph: {
|
|
408
|
-
title: post.title,
|
|
409
|
-
description: post.excerpt,
|
|
410
|
-
images: [post.coverImage],
|
|
411
|
-
},
|
|
412
|
-
twitter: {
|
|
413
|
-
card: "summary_large_image",
|
|
414
|
-
title: post.title,
|
|
415
|
-
},
|
|
416
|
-
alternates: {
|
|
417
|
-
canonical: `https://example.com/blog/${params.slug}`,
|
|
418
|
-
},
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// ❌ HALLUCINATION TRAP: generateMetadata is an async function exported
|
|
423
|
-
// from page.tsx or layout.tsx — NOT a React component.
|
|
424
|
-
// It runs on the server during rendering.
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
### `generateStaticParams` (Static Generation)
|
|
428
|
-
|
|
429
|
-
```tsx
|
|
430
|
-
// Pre-generate pages at build time (SSG)
|
|
431
|
-
export async function generateStaticParams() {
|
|
432
|
-
const posts = await getAllPosts();
|
|
433
|
-
|
|
434
|
-
return posts.map((post) => ({
|
|
435
|
-
slug: post.slug, // matches [slug] dynamic segment
|
|
436
|
-
}));
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
// Combined with dynamicParams:
|
|
440
|
-
export const dynamicParams = false; // 404 for unknown slugs
|
|
441
|
-
// OR
|
|
442
|
-
export const dynamicParams = true; // generate on-demand (default)
|
|
443
|
-
```
|
|
444
|
-
|
|
445
|
-
---
|
|
446
|
-
|
|
447
|
-
## Route Handlers (API Routes)
|
|
448
|
-
|
|
449
|
-
```tsx
|
|
450
|
-
// app/api/users/route.ts
|
|
451
|
-
import { NextRequest, NextResponse } from "next/server";
|
|
452
|
-
|
|
453
|
-
export async function GET(request: NextRequest) {
|
|
454
|
-
const searchParams = request.nextUrl.searchParams;
|
|
455
|
-
const page = searchParams.get("page") ?? "1";
|
|
456
|
-
|
|
457
|
-
const users = await db.user.findMany({
|
|
458
|
-
skip: (parseInt(page) - 1) * 20,
|
|
459
|
-
take: 20,
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
return NextResponse.json(users);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
export async function POST(request: NextRequest) {
|
|
466
|
-
const body = await request.json();
|
|
467
|
-
|
|
468
|
-
// Always validate input
|
|
469
|
-
const parsed = UserSchema.safeParse(body);
|
|
470
|
-
if (!parsed.success) {
|
|
471
|
-
return NextResponse.json(
|
|
472
|
-
{ error: parsed.error.flatten() },
|
|
473
|
-
{ status: 400 }
|
|
474
|
-
);
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
const user = await db.user.create({ data: parsed.data });
|
|
478
|
-
return NextResponse.json(user, { status: 201 });
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
// Dynamic route: app/api/users/[id]/route.ts
|
|
482
|
-
export async function GET(
|
|
483
|
-
request: NextRequest,
|
|
484
|
-
{ params }: { params: { id: string } }
|
|
485
|
-
) {
|
|
486
|
-
const user = await db.user.findUnique({ where: { id: params.id } });
|
|
487
|
-
if (!user) {
|
|
488
|
-
return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
489
|
-
}
|
|
490
|
-
return NextResponse.json(user);
|
|
160
|
+
</div>
|
|
161
|
+
)
|
|
491
162
|
}
|
|
492
|
-
|
|
493
|
-
// ❌ HALLUCINATION TRAP: Route handlers use named exports (GET, POST, PUT, DELETE)
|
|
494
|
-
// NOT default export. NOT export function handler().
|
|
495
|
-
// ❌ HALLUCINATION TRAP: Route handlers are in route.ts, NOT in page.tsx
|
|
496
|
-
// A directory cannot have both page.tsx and route.ts
|
|
497
163
|
```
|
|
498
164
|
|
|
499
165
|
---
|
|
500
166
|
|
|
501
167
|
## Middleware
|
|
502
168
|
|
|
503
|
-
```
|
|
504
|
-
// middleware.ts (
|
|
169
|
+
```typescript
|
|
170
|
+
// middleware.ts (Root of project)
|
|
505
171
|
import { NextResponse } from "next/server";
|
|
506
172
|
import type { NextRequest } from "next/server";
|
|
507
173
|
|
|
508
|
-
export function middleware(
|
|
509
|
-
const token =
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
// Redirect unauthenticated users
|
|
513
|
-
if (pathname.startsWith("/dashboard") && !token) {
|
|
514
|
-
return NextResponse.redirect(new URL("/login", request.url));
|
|
174
|
+
export function middleware(req: NextRequest) {
|
|
175
|
+
const token = req.cookies.get("auth-token");
|
|
176
|
+
if (!token && req.nextUrl.pathname.startsWith("/dashboard")) {
|
|
177
|
+
return NextResponse.redirect(new URL("/login", req.url));
|
|
515
178
|
}
|
|
516
|
-
|
|
517
|
-
// Add custom headers
|
|
518
|
-
const response = NextResponse.next();
|
|
519
|
-
response.headers.set("x-pathname", pathname);
|
|
520
|
-
|
|
521
|
-
return response;
|
|
522
179
|
}
|
|
523
180
|
|
|
524
|
-
// Matcher: only run middleware on specific routes
|
|
525
181
|
export const config = {
|
|
526
|
-
matcher: [
|
|
527
|
-
"/dashboard/:path*",
|
|
528
|
-
"/api/:path*",
|
|
529
|
-
// Skip static files and Next.js internals
|
|
530
|
-
"/((?!_next/static|_next/image|favicon.ico).*)",
|
|
531
|
-
],
|
|
182
|
+
matcher: ["/dashboard/:path*"], // Strict matcher is critical for performance
|
|
532
183
|
};
|
|
533
|
-
|
|
534
|
-
// ❌ HALLUCINATION TRAP: middleware.ts must be at the project ROOT
|
|
535
|
-
// (same level as app/ directory), NOT inside app/
|
|
536
|
-
// ❌ HALLUCINATION TRAP: Middleware runs on the Edge Runtime
|
|
537
|
-
// You CANNOT use Node.js APIs (fs, crypto.createHash, etc.)
|
|
538
|
-
// Use Web APIs (crypto.subtle, fetch, Response, Headers)
|
|
539
|
-
```
|
|
540
|
-
|
|
541
|
-
---
|
|
542
|
-
|
|
543
|
-
## Parallel & Intercepting Routes
|
|
544
|
-
|
|
545
|
-
### Parallel Routes (Named Slots)
|
|
546
|
-
|
|
547
|
-
```
|
|
548
|
-
app/
|
|
549
|
-
├── layout.tsx
|
|
550
|
-
├── page.tsx
|
|
551
|
-
├── @analytics/
|
|
552
|
-
│ └── page.tsx ← Rendered in parallel with main page
|
|
553
|
-
└── @notifications/
|
|
554
|
-
└── page.tsx ← Also rendered in parallel
|
|
555
|
-
```
|
|
556
|
-
|
|
557
|
-
```tsx
|
|
558
|
-
// app/layout.tsx
|
|
559
|
-
export default function Layout({
|
|
560
|
-
children,
|
|
561
|
-
analytics,
|
|
562
|
-
notifications,
|
|
563
|
-
}: {
|
|
564
|
-
children: React.ReactNode;
|
|
565
|
-
analytics: React.ReactNode;
|
|
566
|
-
notifications: React.ReactNode;
|
|
567
|
-
}) {
|
|
568
|
-
return (
|
|
569
|
-
<div>
|
|
570
|
-
<main>{children}</main>
|
|
571
|
-
<aside>{analytics}</aside>
|
|
572
|
-
<div>{notifications}</div>
|
|
573
|
-
</div>
|
|
574
|
-
);
|
|
575
|
-
}
|
|
576
|
-
```
|
|
577
|
-
|
|
578
|
-
### Intercepting Routes (Modal Pattern)
|
|
579
|
-
|
|
580
|
-
```
|
|
581
|
-
app/
|
|
582
|
-
├── feed/
|
|
583
|
-
│ └── page.tsx ← /feed (shows feed)
|
|
584
|
-
├── photo/[id]/
|
|
585
|
-
│ └── page.tsx ← /photo/123 (full photo page)
|
|
586
|
-
└── @modal/
|
|
587
|
-
└── (..)photo/[id]/
|
|
588
|
-
└── page.tsx ← Intercepts /photo/123 when navigating from /feed
|
|
589
|
-
Shows as modal overlay instead of full page
|
|
590
|
-
```
|
|
591
|
-
|
|
592
184
|
```
|
|
593
|
-
Intercepting conventions:
|
|
594
|
-
(.) — same level
|
|
595
|
-
(..) — one level up
|
|
596
|
-
(..)(..) — two levels up
|
|
597
|
-
(...) — from root
|
|
598
|
-
```
|
|
599
|
-
|
|
600
|
-
---
|
|
601
|
-
|
|
602
|
-
## AI & Streaming UI
|
|
603
|
-
|
|
604
|
-
```tsx
|
|
605
|
-
import { openai } from "@ai-sdk/openai";
|
|
606
|
-
import { streamText } from "ai";
|
|
607
|
-
|
|
608
|
-
// app/api/chat/route.ts
|
|
609
|
-
export async function POST(req: NextRequest) {
|
|
610
|
-
const { messages } = await req.json();
|
|
611
|
-
|
|
612
|
-
const result = streamText({
|
|
613
|
-
model: openai("gpt-4o"),
|
|
614
|
-
messages,
|
|
615
|
-
});
|
|
616
|
-
|
|
617
|
-
return result.toDataStreamResponse();
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
// Client component:
|
|
621
|
-
"use client";
|
|
622
|
-
import { useChat } from "@ai-sdk/react";
|
|
623
|
-
|
|
624
|
-
function ChatUI() {
|
|
625
|
-
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat();
|
|
626
|
-
|
|
627
|
-
return (
|
|
628
|
-
<div>
|
|
629
|
-
{messages.map((m) => (
|
|
630
|
-
<div key={m.id} className={m.role === "user" ? "user" : "ai"}>
|
|
631
|
-
{m.content}
|
|
632
|
-
</div>
|
|
633
|
-
))}
|
|
634
|
-
<form onSubmit={handleSubmit}>
|
|
635
|
-
<input value={input} onChange={handleInputChange} />
|
|
636
|
-
<button disabled={isLoading}>Send</button>
|
|
637
|
-
</form>
|
|
638
|
-
</div>
|
|
639
|
-
);
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
// ❌ HALLUCINATION TRAP: Import from "@ai-sdk/react", NOT "ai/react"
|
|
643
|
-
// The package was restructured in Vercel AI SDK 4+
|
|
644
|
-
```
|
|
645
|
-
|
|
646
|
-
---
|
|
647
|
-
|
|
648
|
-
## Bundle Optimization
|
|
649
|
-
|
|
650
|
-
```tsx
|
|
651
|
-
// 1. Push "use client" as FAR DOWN as possible
|
|
652
|
-
// ❌ BAD: "use client" on layout.tsx (ships entire layout as JS)
|
|
653
|
-
// ✅ GOOD: "use client" only on the interactive widget component
|
|
654
|
-
|
|
655
|
-
// 2. Dynamic imports for heavy client deps
|
|
656
|
-
import dynamic from "next/dynamic";
|
|
657
|
-
|
|
658
|
-
const HeavyChart = dynamic(() => import("./HeavyChart"), {
|
|
659
|
-
ssr: false, // skip server rendering
|
|
660
|
-
loading: () => <ChartSkeleton />,
|
|
661
|
-
});
|
|
662
|
-
|
|
663
|
-
// 3. next/image for automatic optimization
|
|
664
|
-
import Image from "next/image";
|
|
665
|
-
|
|
666
|
-
<Image
|
|
667
|
-
src="/hero.jpg"
|
|
668
|
-
alt="Hero image"
|
|
669
|
-
width={1200}
|
|
670
|
-
height={630}
|
|
671
|
-
priority // preload for LCP images
|
|
672
|
-
placeholder="blur"
|
|
673
|
-
blurDataURL={blurUrl}
|
|
674
|
-
/>
|
|
675
|
-
|
|
676
|
-
// 4. next/font for zero-layout-shift fonts
|
|
677
|
-
import { Inter } from "next/font/google";
|
|
678
|
-
|
|
679
|
-
const inter = Inter({
|
|
680
|
-
subsets: ["latin"],
|
|
681
|
-
display: "swap",
|
|
682
|
-
variable: "--font-inter",
|
|
683
|
-
});
|
|
684
|
-
|
|
685
|
-
// In layout.tsx:
|
|
686
|
-
<html className={inter.variable}>
|
|
687
|
-
<body>{children}</body>
|
|
688
|
-
</html>
|
|
689
|
-
|
|
690
|
-
// ❌ HALLUCINATION TRAP: next/font automatically self-hosts fonts
|
|
691
|
-
// Do NOT add Google Fonts <link> tags in <head> — they cause CLS
|
|
692
|
-
```
|
|
693
|
-
|
|
694
|
-
---
|
|
695
|
-
|
|
696
|
-
## Key Anti-Patterns
|
|
697
|
-
|
|
698
|
-
| Pattern | Problem | Fix |
|
|
699
|
-
|---|---|---|
|
|
700
|
-
| `getServerSideProps` in App Router | Pages Router API — doesn't exist | Fetch directly in Server Components |
|
|
701
|
-
| `"use client"` on layout/page | Ships massive JS bundle | Push `"use client"` to leaf components |
|
|
702
|
-
| `useEffect(() => fetch(...))` | Client waterfall, no cache, CLS | Server Component or React Query |
|
|
703
|
-
| Sequential `await` calls | Network waterfall | `Promise.all()` or `<Suspense>` |
|
|
704
|
-
| `cookies()`/`headers()` at top level | Disables PPR for entire route | Isolate inside `<Suspense>` boundaries |
|
|
705
|
-
| `next/router` (Pages Router) | Wrong import | Use `next/navigation` |
|
|
706
|
-
| Missing `loading.tsx` | Blank screen during navigation | Add loading.tsx or Suspense |
|
|
707
|
-
| Raw `<img>` and `<a>` tags | No optimization | Use `next/image` and `next/link` |
|
|
708
|
-
|
|
709
|
-
---
|
|
710
|
-
|
|
711
|
-
## Output Format
|
|
712
|
-
|
|
713
|
-
When this skill produces or reviews code, structure your output as follows:
|
|
714
|
-
|
|
715
|
-
```
|
|
716
|
-
━━━ Next.js Expert Report ━━━━━━━━━━━━━━━━━━━━━━━━
|
|
717
|
-
Skill: Next.js React Expert
|
|
718
|
-
Next.js Ver: 15+
|
|
719
|
-
Scope: [N files · N routes]
|
|
720
|
-
─────────────────────────────────────────────────
|
|
721
|
-
✅ Passed: [checks that passed, or "All clean"]
|
|
722
|
-
⚠️ Warnings: [non-blocking issues, or "None"]
|
|
723
|
-
❌ Blocked: [blocking issues requiring fix, or "None"]
|
|
724
|
-
─────────────────────────────────────────────────
|
|
725
|
-
VBC status: PENDING → VERIFIED
|
|
726
|
-
Evidence: [test output / lint pass / compile success]
|
|
727
|
-
```
|
|
728
|
-
|
|
729
|
-
**VBC (Verification-Before-Completion) is mandatory.**
|
|
730
|
-
Do not mark status as VERIFIED until concrete terminal evidence is provided.
|
|
731
|
-
|
|
732
|
-
---
|
|
733
|
-
|
|
734
|
-
## 🤖 LLM-Specific Traps
|
|
735
|
-
|
|
736
|
-
AI coding assistants often fall into specific bad habits when generating Next.js code. These are strictly forbidden:
|
|
737
|
-
|
|
738
|
-
1. **Pages Router in App Router:** Never generate `getServerSideProps`, `getStaticProps`, `getInitialProps`, `_app.tsx`, `_document.tsx`, or `pages/api/` in an App Router project.
|
|
739
|
-
2. **`"use client"` Everywhere:** Marking layouts, pages, or entire feature modules as client components defeats RSC. Push the `"use client"` boundary as deep as possible.
|
|
740
|
-
3. **`next/router` Import:** The Pages Router `useRouter` is `next/router`. App Router uses `next/navigation`. Using the wrong one causes runtime errors.
|
|
741
|
-
4. **Missing Input Validation in Server Actions:** Server Actions receive raw `FormData` from the client. Always validate with Zod or similar before touching the database.
|
|
742
|
-
5. **`useEffect` for Data Fetching:** Server Components can fetch directly. Client components should use React Query or SWR. `useEffect` fetch has no caching, no deduplication, no error boundaries.
|
|
743
|
-
6. **Forgetting Next.js 15 Cache Changes:** In Next.js 15, `fetch()` is NOT cached by default (changed from 14). You must explicitly opt into caching.
|
|
744
|
-
7. **Google Fonts `<link>` Tags:** Never add external font `<link>` tags. Use `next/font` for zero-CLS, self-hosted fonts.
|
|
745
|
-
8. **Route Handler Default Exports:** Route handlers use named exports (`GET`, `POST`, `DELETE`), not `export default function handler`.
|
|
746
|
-
9. **Middleware Inside `app/`:** `middleware.ts` must be at the project root, not inside the `app/` directory.
|
|
747
|
-
10. **`"ai/react"` Import Path:** The Vercel AI SDK restructured its exports. Use `@ai-sdk/react` for hooks and `ai` for core.
|
|
748
|
-
|
|
749
|
-
---
|
|
750
|
-
|
|
751
|
-
## 🏛️ Tribunal Integration (Anti-Hallucination)
|
|
752
|
-
|
|
753
|
-
**Slash command: `/tribunal-frontend`**
|
|
754
|
-
**Active reviewers: `logic` · `security` · `frontend` · `type-safety`**
|
|
755
|
-
|
|
756
|
-
### ❌ Forbidden AI Tropes
|
|
757
|
-
|
|
758
|
-
1. **Blind Assumptions:** Never make an assumption without documenting it clearly with `// VERIFY: [reason]`.
|
|
759
|
-
2. **Silent Degradation:** Catching and suppressing errors without logging or displaying error boundaries.
|
|
760
|
-
3. **Context Amnesia:** Forgetting whether the project uses Pages Router or App Router.
|
|
761
|
-
4. **Generic Design:** Do not default to black/white Vercel aesthetics unless instructed.
|
|
762
|
-
|
|
763
|
-
### ✅ Pre-Flight Self-Audit
|
|
764
|
-
|
|
765
|
-
Review these questions before confirming output:
|
|
766
|
-
```
|
|
767
|
-
✅ Did I maximize Server Component usage and isolate "use client"?
|
|
768
|
-
✅ Are there sequential awaits creating a waterfall? Did I use Promise.all or Suspense?
|
|
769
|
-
✅ Did I validate all Server Action inputs with Zod?
|
|
770
|
-
✅ Did I use next/image and next/link (not raw <img> and <a>)?
|
|
771
|
-
✅ Did I implement loading.tsx and error.tsx for route segments?
|
|
772
|
-
✅ Did I use next/font (not external font <link> tags)?
|
|
773
|
-
✅ Did I use next/navigation (not next/router)?
|
|
774
|
-
✅ Are dynamic data reads (cookies, headers) inside Suspense for PPR?
|
|
775
|
-
✅ Did I add generateMetadata for SEO?
|
|
776
|
-
✅ Is middleware.ts at the project root (not inside app/)?
|
|
777
|
-
```
|
|
778
|
-
|
|
779
|
-
### 🛑 Verification-Before-Completion (VBC) Protocol
|
|
780
|
-
|
|
781
|
-
**CRITICAL:** You must follow a strict "evidence-based closeout" state machine.
|
|
782
|
-
- ❌ **Forbidden:** Assuming a Next.js route "works" because the dev server shows no errors.
|
|
783
|
-
- ✅ **Required:** You are explicitly forbidden from completing your task without providing **concrete evidence** (successful `next build`, passing tests, or equivalent proof) that the code compiles and runs correctly.
|