@balazsbarta/mp-skills 0.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/.agents/skills/brainstorming/SKILL.md +201 -0
- package/.agents/skills/brainstorming/references/option-evaluation.md +64 -0
- package/.agents/skills/brainstorming/references/questioning-playbook.md +57 -0
- package/.agents/skills/brainstorming/references/repo-analysis.md +60 -0
- package/.agents/skills/conventional-commits/SKILL.md +124 -0
- package/.agents/skills/conventional-commits/references/commit-types-scopes.md +75 -0
- package/.agents/skills/conventional-commits/references/semantic-release.md +71 -0
- package/.agents/skills/jest/SKILL.md +219 -0
- package/.agents/skills/jest/references/common-errors.md +274 -0
- package/.agents/skills/jest/references/configuration.md +175 -0
- package/.agents/skills/jest/references/embedded-docs.md +44 -0
- package/.agents/skills/jest/references/mocking.md +206 -0
- package/.agents/skills/jest/references/remote-docs.md +19 -0
- package/.agents/skills/jest/references/snapshot-testing.md +181 -0
- package/.agents/skills/jest/references/transforms.md +216 -0
- package/.agents/skills/maestro/SKILL.md +230 -0
- package/.agents/skills/maestro/references/assertions-commands.md +259 -0
- package/.agents/skills/maestro/references/common-errors.md +273 -0
- package/.agents/skills/maestro/references/eas-ci-integration.md +219 -0
- package/.agents/skills/maestro/references/flow-authoring.md +224 -0
- package/.agents/skills/maestro/references/remote-docs.md +23 -0
- package/.agents/skills/mastra/SKILL.md +159 -0
- package/.agents/skills/mastra/references/common-errors.md +535 -0
- package/.agents/skills/mastra/references/create-mastra.md +220 -0
- package/.agents/skills/mastra/references/embedded-docs.md +123 -0
- package/.agents/skills/mastra/references/migration-guide.md +180 -0
- package/.agents/skills/mastra/references/remote-docs.md +193 -0
- package/.agents/skills/next-js/SKILL.md +209 -0
- package/.agents/skills/next-js/references/api-routes.md +213 -0
- package/.agents/skills/next-js/references/app-router.md +206 -0
- package/.agents/skills/next-js/references/caching-revalidation.md +211 -0
- package/.agents/skills/next-js/references/common-errors.md +251 -0
- package/.agents/skills/next-js/references/embedded-docs.md +43 -0
- package/.agents/skills/next-js/references/metadata-seo.md +257 -0
- package/.agents/skills/next-js/references/remote-docs.md +22 -0
- package/.agents/skills/playwright/SKILL.md +218 -0
- package/.agents/skills/playwright/references/ci-configuration.md +208 -0
- package/.agents/skills/playwright/references/common-errors.md +258 -0
- package/.agents/skills/playwright/references/embedded-docs.md +41 -0
- package/.agents/skills/playwright/references/fixtures-assertions.md +208 -0
- package/.agents/skills/playwright/references/page-objects.md +167 -0
- package/.agents/skills/playwright/references/remote-docs.md +23 -0
- package/.agents/skills/playwright/references/visual-regression.md +206 -0
- package/.agents/skills/pull-request-lifecycle/SKILL.md +116 -0
- package/.agents/skills/pull-request-lifecycle/references/changelog-versioning.md +72 -0
- package/.agents/skills/pull-request-lifecycle/references/merge-strategies.md +33 -0
- package/.agents/skills/pull-request-lifecycle/references/pr-description-template.md +72 -0
- package/.agents/skills/pull-request-lifecycle/references/review-process.md +54 -0
- package/.agents/skills/pull-request-lifecycle/scripts/code_review.py +220 -0
- package/.agents/skills/react-native-expo/SKILL.md +212 -0
- package/.agents/skills/react-native-expo/references/common-errors.md +251 -0
- package/.agents/skills/react-native-expo/references/eas-build-submit.md +238 -0
- package/.agents/skills/react-native-expo/references/embedded-docs.md +42 -0
- package/.agents/skills/react-native-expo/references/native-modules.md +181 -0
- package/.agents/skills/react-native-expo/references/navigation-setup.md +229 -0
- package/.agents/skills/react-native-expo/references/remote-docs.md +23 -0
- package/.agents/skills/supabase/SKILL.md +216 -0
- package/.agents/skills/supabase/references/auth-setup.md +206 -0
- package/.agents/skills/supabase/references/common-errors.md +285 -0
- package/.agents/skills/supabase/references/edge-functions.md +178 -0
- package/.agents/skills/supabase/references/embedded-docs.md +43 -0
- package/.agents/skills/supabase/references/migrations.md +193 -0
- package/.agents/skills/supabase/references/remote-docs.md +24 -0
- package/.agents/skills/supabase/references/rls-policies.md +187 -0
- package/.agents/skills/supabase/references/storage.md +182 -0
- package/.agents/skills/task-breakdown/SKILL.md +179 -0
- package/.agents/skills/task-breakdown/references/acceptance-criteria.md +165 -0
- package/.agents/skills/task-breakdown/references/epic-story-format.md +209 -0
- package/.agents/skills/task-breakdown/references/estimation-guide.md +140 -0
- package/.agents/skills/vitest/SKILL.md +219 -0
- package/.agents/skills/vitest/references/common-errors.md +271 -0
- package/.agents/skills/vitest/references/component-testing.md +182 -0
- package/.agents/skills/vitest/references/configuration.md +184 -0
- package/.agents/skills/vitest/references/coverage.md +179 -0
- package/.agents/skills/vitest/references/embedded-docs.md +43 -0
- package/.agents/skills/vitest/references/mocking.md +182 -0
- package/.agents/skills/vitest/references/remote-docs.md +22 -0
- package/README.md +235 -0
- package/package.json +20 -0
- package/scripts/skills.mjs +849 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# App Router
|
|
2
|
+
|
|
3
|
+
Next.js App Router file conventions, layouts, pages, and special files.
|
|
4
|
+
|
|
5
|
+
## When to use this reference
|
|
6
|
+
|
|
7
|
+
- Creating pages and layouts with the App Router
|
|
8
|
+
- Understanding file conventions (loading, error, not-found)
|
|
9
|
+
- Working with dynamic routes and route groups
|
|
10
|
+
|
|
11
|
+
## File conventions
|
|
12
|
+
|
|
13
|
+
| File | Purpose | Required |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `layout.tsx` | Shared layout (persists across navigations) | Root only |
|
|
16
|
+
| `page.tsx` | Unique page content | Yes (for routes) |
|
|
17
|
+
| `loading.tsx` | Loading UI (Suspense boundary) | No |
|
|
18
|
+
| `error.tsx` | Error boundary | No |
|
|
19
|
+
| `not-found.tsx` | 404 page | No |
|
|
20
|
+
| `template.tsx` | Re-renders on navigation (no persist) | No |
|
|
21
|
+
| `default.tsx` | Parallel route fallback | No |
|
|
22
|
+
|
|
23
|
+
## Pages
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
// app/page.tsx — Home page at /
|
|
27
|
+
export default function HomePage() {
|
|
28
|
+
return <h1>Home</h1>;
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Server Component (default)
|
|
33
|
+
|
|
34
|
+
Pages are Server Components by default — they can fetch data directly:
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
// app/users/page.tsx
|
|
38
|
+
async function getUsers() {
|
|
39
|
+
const res = await fetch('https://api.example.com/users');
|
|
40
|
+
return res.json();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default async function UsersPage() {
|
|
44
|
+
const users = await getUsers();
|
|
45
|
+
return (
|
|
46
|
+
<ul>
|
|
47
|
+
{users.map((user) => (
|
|
48
|
+
<li key={user.id}>{user.name}</li>
|
|
49
|
+
))}
|
|
50
|
+
</ul>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Layouts
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// app/layout.tsx — Root layout
|
|
59
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
60
|
+
return (
|
|
61
|
+
<html lang="en">
|
|
62
|
+
<body>
|
|
63
|
+
<nav>Navigation</nav>
|
|
64
|
+
<main>{children}</main>
|
|
65
|
+
</body>
|
|
66
|
+
</html>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Nested layouts
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
// app/dashboard/layout.tsx — Dashboard layout
|
|
75
|
+
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
|
|
76
|
+
return (
|
|
77
|
+
<div className="flex">
|
|
78
|
+
<aside>Sidebar</aside>
|
|
79
|
+
<section>{children}</section>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Dynamic routes
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// app/blog/[slug]/page.tsx
|
|
89
|
+
export default async function BlogPost({ params }: { params: Promise<{ slug: string }> }) {
|
|
90
|
+
const { slug } = await params;
|
|
91
|
+
return <h1>Post: {slug}</h1>;
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Catch-all routes
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
// app/docs/[...slug]/page.tsx — matches /docs/a, /docs/a/b, etc.
|
|
99
|
+
export default async function DocsPage({ params }: { params: Promise<{ slug: string[] }> }) {
|
|
100
|
+
const { slug } = await params;
|
|
101
|
+
return <h1>Docs: {slug.join('/')}</h1>;
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Route groups
|
|
106
|
+
|
|
107
|
+
Organize routes without affecting the URL:
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
app/
|
|
111
|
+
├── (marketing)/
|
|
112
|
+
│ ├── layout.tsx # Marketing layout
|
|
113
|
+
│ ├── page.tsx # / (home)
|
|
114
|
+
│ └── about/page.tsx # /about
|
|
115
|
+
├── (app)/
|
|
116
|
+
│ ├── layout.tsx # App layout (different from marketing)
|
|
117
|
+
│ └── dashboard/page.tsx # /dashboard
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Loading and error states
|
|
121
|
+
|
|
122
|
+
### Loading
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
// app/dashboard/loading.tsx
|
|
126
|
+
export default function Loading() {
|
|
127
|
+
return <div>Loading dashboard...</div>;
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Error boundary
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
// app/dashboard/error.tsx
|
|
135
|
+
'use client'; // Error boundaries must be Client Components
|
|
136
|
+
|
|
137
|
+
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
|
138
|
+
return (
|
|
139
|
+
<div>
|
|
140
|
+
<h2>Something went wrong!</h2>
|
|
141
|
+
<button onClick={reset}>Try again</button>
|
|
142
|
+
</div>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Not found
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// app/not-found.tsx
|
|
151
|
+
export default function NotFound() {
|
|
152
|
+
return <h1>404 — Page Not Found</h1>;
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Static generation with generateStaticParams
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
// app/blog/[slug]/page.tsx
|
|
160
|
+
export async function generateStaticParams() {
|
|
161
|
+
const posts = await getPosts();
|
|
162
|
+
return posts.map((post) => ({ slug: post.slug }));
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Parallel routes
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
app/
|
|
170
|
+
├── @analytics/page.tsx
|
|
171
|
+
├── @team/page.tsx
|
|
172
|
+
└── layout.tsx
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
// app/layout.tsx
|
|
177
|
+
export default function Layout({
|
|
178
|
+
children,
|
|
179
|
+
analytics,
|
|
180
|
+
team,
|
|
181
|
+
}: {
|
|
182
|
+
children: React.ReactNode;
|
|
183
|
+
analytics: React.ReactNode;
|
|
184
|
+
team: React.ReactNode;
|
|
185
|
+
}) {
|
|
186
|
+
return (
|
|
187
|
+
<>
|
|
188
|
+
{children}
|
|
189
|
+
{analytics}
|
|
190
|
+
{team}
|
|
191
|
+
</>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Troubleshooting
|
|
197
|
+
|
|
198
|
+
### Page not rendering
|
|
199
|
+
|
|
200
|
+
- Ensure file is named `page.tsx` (not `Page.tsx` or `index.tsx`)
|
|
201
|
+
- Check that the directory structure matches the desired URL
|
|
202
|
+
|
|
203
|
+
### Layout not applying
|
|
204
|
+
|
|
205
|
+
- Layouts only apply to child routes, not sibling routes
|
|
206
|
+
- Root layout must include `<html>` and `<body>` tags
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# Caching and Revalidation
|
|
2
|
+
|
|
3
|
+
Next.js caching layers and revalidation strategies.
|
|
4
|
+
|
|
5
|
+
## When to use this reference
|
|
6
|
+
|
|
7
|
+
- Understanding how Next.js caches data and pages
|
|
8
|
+
- Configuring revalidation (ISR, on-demand)
|
|
9
|
+
- Debugging caching behavior
|
|
10
|
+
- Opting out of caching
|
|
11
|
+
|
|
12
|
+
## Caching layers
|
|
13
|
+
|
|
14
|
+
| Layer | What | Where | Default |
|
|
15
|
+
| --- | --- | --- | --- |
|
|
16
|
+
| Request Memoization | `fetch` deduplication | Server (per request) | On |
|
|
17
|
+
| Data Cache | `fetch` responses | Server (persistent) | Check version |
|
|
18
|
+
| Full Route Cache | Rendered HTML + RSC payload | Server (persistent) | Static routes |
|
|
19
|
+
| Router Cache | RSC payload | Client (session) | On |
|
|
20
|
+
|
|
21
|
+
**Important:** Caching defaults changed in Next.js 15. Check your version.
|
|
22
|
+
|
|
23
|
+
## fetch caching
|
|
24
|
+
|
|
25
|
+
### Next.js 14 (cached by default)
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// Cached by default
|
|
29
|
+
const res = await fetch('https://api.example.com/data');
|
|
30
|
+
|
|
31
|
+
// Opt out of cache
|
|
32
|
+
const res = await fetch('https://api.example.com/data', { cache: 'no-store' });
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Next.js 15+ (not cached by default)
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// NOT cached by default
|
|
39
|
+
const res = await fetch('https://api.example.com/data');
|
|
40
|
+
|
|
41
|
+
// Opt into cache
|
|
42
|
+
const res = await fetch('https://api.example.com/data', { cache: 'force-cache' });
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Time-based revalidation (ISR)
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
// Revalidate every 60 seconds
|
|
49
|
+
const res = await fetch('https://api.example.com/data', {
|
|
50
|
+
next: { revalidate: 60 },
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Page-level revalidation
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// app/blog/page.tsx
|
|
58
|
+
export const revalidate = 60; // Revalidate this page every 60 seconds
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## On-demand revalidation
|
|
62
|
+
|
|
63
|
+
### By path
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// app/api/revalidate/route.ts
|
|
67
|
+
import { revalidatePath } from 'next/cache';
|
|
68
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
69
|
+
|
|
70
|
+
export async function POST(request: NextRequest) {
|
|
71
|
+
const { path } = await request.json();
|
|
72
|
+
revalidatePath(path);
|
|
73
|
+
return NextResponse.json({ revalidated: true });
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### By tag
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
// Fetch with tag
|
|
81
|
+
const res = await fetch('https://api.example.com/posts', {
|
|
82
|
+
next: { tags: ['posts'] },
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Revalidate by tag
|
|
86
|
+
import { revalidateTag } from 'next/cache';
|
|
87
|
+
revalidateTag('posts');
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Route segment config
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
// Force static (default for pages without dynamic data)
|
|
94
|
+
export const dynamic = 'auto';
|
|
95
|
+
|
|
96
|
+
// Force dynamic (disable caching)
|
|
97
|
+
export const dynamic = 'force-dynamic';
|
|
98
|
+
|
|
99
|
+
// Force static generation (error if dynamic features used)
|
|
100
|
+
export const dynamic = 'force-static';
|
|
101
|
+
|
|
102
|
+
// Error if dynamic features used
|
|
103
|
+
export const dynamic = 'error';
|
|
104
|
+
|
|
105
|
+
// Set revalidation period
|
|
106
|
+
export const revalidate = 60; // seconds
|
|
107
|
+
|
|
108
|
+
// Set runtime
|
|
109
|
+
export const runtime = 'nodejs'; // or 'edge'
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## `unstable_cache` (for non-fetch data)
|
|
113
|
+
|
|
114
|
+
Cache data that doesn't come from `fetch`:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
import { unstable_cache } from 'next/cache';
|
|
118
|
+
|
|
119
|
+
const getCachedUser = unstable_cache(
|
|
120
|
+
async (id: string) => {
|
|
121
|
+
return db.user.findUnique({ where: { id } });
|
|
122
|
+
},
|
|
123
|
+
['user'], // cache key prefix
|
|
124
|
+
{
|
|
125
|
+
revalidate: 3600, // 1 hour
|
|
126
|
+
tags: ['user'],
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## `cache` function (request memoization)
|
|
132
|
+
|
|
133
|
+
Deduplicate data fetching within a single request:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
import { cache } from 'react';
|
|
137
|
+
|
|
138
|
+
export const getUser = cache(async (id: string) => {
|
|
139
|
+
const res = await fetch(`/api/users/${id}`);
|
|
140
|
+
return res.json();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Call getUser(id) multiple times in different components —
|
|
144
|
+
// only one fetch happens per request
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Debugging caching
|
|
148
|
+
|
|
149
|
+
### Check build output
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
npx next build
|
|
153
|
+
# Look for route types:
|
|
154
|
+
# ○ Static
|
|
155
|
+
# ● SSG (ISR)
|
|
156
|
+
# λ Dynamic (server-rendered)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Headers to check
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// Check cache status in response headers
|
|
163
|
+
// x-nextjs-cache: HIT | MISS | STALE
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Disable all caching (development)
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
// next.config.js
|
|
170
|
+
module.exports = {
|
|
171
|
+
experimental: {
|
|
172
|
+
staleTimes: { dynamic: 0, static: 0 },
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Common patterns
|
|
178
|
+
|
|
179
|
+
### Static page with periodic revalidation (ISR)
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
export const revalidate = 3600; // Revalidate every hour
|
|
183
|
+
|
|
184
|
+
export default async function Page() {
|
|
185
|
+
const data = await fetch('https://api.example.com/data');
|
|
186
|
+
return <div>{/* render data */}</div>;
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Dynamic page (no cache)
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
export const dynamic = 'force-dynamic';
|
|
194
|
+
|
|
195
|
+
export default async function Page() {
|
|
196
|
+
const data = await fetch('https://api.example.com/data');
|
|
197
|
+
return <div>{/* render data */}</div>;
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Troubleshooting
|
|
202
|
+
|
|
203
|
+
### Stale data after deploy
|
|
204
|
+
|
|
205
|
+
- Use `revalidatePath` or `revalidateTag` for on-demand updates
|
|
206
|
+
- Check if `revalidate` is set too high
|
|
207
|
+
|
|
208
|
+
### Page always dynamic when you want static
|
|
209
|
+
|
|
210
|
+
- Remove `cookies()`, `headers()`, or `searchParams` usage
|
|
211
|
+
- These force dynamic rendering
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# Next.js Common Errors
|
|
2
|
+
|
|
3
|
+
Solutions for frequently encountered Next.js errors.
|
|
4
|
+
|
|
5
|
+
## When to use this reference
|
|
6
|
+
|
|
7
|
+
- Debugging build or runtime errors
|
|
8
|
+
- Fixing hydration mismatches
|
|
9
|
+
- Resolving Server/Client Component issues
|
|
10
|
+
|
|
11
|
+
## Server/Client Component errors
|
|
12
|
+
|
|
13
|
+
### "useState is not a function" / "Hooks can only be called inside a component"
|
|
14
|
+
|
|
15
|
+
**Symptoms:**
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Error: useState is not a function
|
|
19
|
+
Error: (0, react__WEBPACK_IMPORTED_MODULE_0__.createContext) is not a function
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Causes:**
|
|
23
|
+
- Using React hooks in a Server Component
|
|
24
|
+
|
|
25
|
+
**Solutions:**
|
|
26
|
+
|
|
27
|
+
Add `'use client'` directive at the top of the file:
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
'use client';
|
|
31
|
+
|
|
32
|
+
import { useState } from 'react';
|
|
33
|
+
|
|
34
|
+
export default function Counter() {
|
|
35
|
+
const [count, setCount] = useState(0);
|
|
36
|
+
return <button onClick={() => setCount(count + 1)}>{count}</button>;
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### "Event handlers cannot be passed to Client Component props"
|
|
41
|
+
|
|
42
|
+
**Symptoms:**
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
Error: Event handlers cannot be passed to Client Component props
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Causes:**
|
|
49
|
+
- Passing `onClick` or other handlers from Server to Client Component
|
|
50
|
+
|
|
51
|
+
**Solutions:**
|
|
52
|
+
|
|
53
|
+
Create a Client Component wrapper:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// ClientButton.tsx
|
|
57
|
+
'use client';
|
|
58
|
+
|
|
59
|
+
export function ClientButton({ label }: { label: string }) {
|
|
60
|
+
return <button onClick={() => console.log('clicked')}>{label}</button>;
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Hydration errors
|
|
65
|
+
|
|
66
|
+
### "Hydration failed because the server rendered HTML didn't match the client"
|
|
67
|
+
|
|
68
|
+
**Symptoms:**
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
Error: Hydration failed because the server rendered HTML didn't match the client
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Causes:**
|
|
75
|
+
- Browser-only code in Server Component render
|
|
76
|
+
- Date/time rendering (server time vs client time)
|
|
77
|
+
- Browser extensions modifying DOM
|
|
78
|
+
- Conditional rendering based on `typeof window`
|
|
79
|
+
|
|
80
|
+
**Solutions:**
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
'use client';
|
|
84
|
+
|
|
85
|
+
import { useState, useEffect } from 'react';
|
|
86
|
+
|
|
87
|
+
export function ClientDate() {
|
|
88
|
+
const [date, setDate] = useState<string>('');
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
setDate(new Date().toLocaleDateString());
|
|
92
|
+
}, []);
|
|
93
|
+
|
|
94
|
+
return <span>{date}</span>;
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Or use `suppressHydrationWarning`:
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
<time suppressHydrationWarning>{new Date().toLocaleDateString()}</time>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Build errors
|
|
105
|
+
|
|
106
|
+
### "Dynamic server usage" errors
|
|
107
|
+
|
|
108
|
+
**Symptoms:**
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
Error: Dynamic server usage: cookies
|
|
112
|
+
Error: Dynamic server usage: headers
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Causes:**
|
|
116
|
+
- Using `cookies()`, `headers()`, or `searchParams` in a statically generated page
|
|
117
|
+
|
|
118
|
+
**Solutions:**
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
// Option 1: Mark as dynamic
|
|
122
|
+
export const dynamic = 'force-dynamic';
|
|
123
|
+
|
|
124
|
+
// Option 2: Use Suspense boundary
|
|
125
|
+
import { Suspense } from 'react';
|
|
126
|
+
|
|
127
|
+
export default function Page() {
|
|
128
|
+
return (
|
|
129
|
+
<Suspense fallback={<div>Loading...</div>}>
|
|
130
|
+
<DynamicContent />
|
|
131
|
+
</Suspense>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### "Module not found: Can't resolve 'fs'"
|
|
137
|
+
|
|
138
|
+
**Symptoms:**
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
Module not found: Can't resolve 'fs'
|
|
142
|
+
Module not found: Can't resolve 'path'
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Causes:**
|
|
146
|
+
- Using Node.js modules in Client Component or shared code
|
|
147
|
+
|
|
148
|
+
**Solutions:**
|
|
149
|
+
|
|
150
|
+
- Move server-only code to Server Components or Route Handlers
|
|
151
|
+
- Use `server-only` package to prevent accidental client imports:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
npm install server-only
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import 'server-only';
|
|
159
|
+
// This file can only be imported in Server Components
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Runtime errors
|
|
163
|
+
|
|
164
|
+
### "Cannot read properties of null (reading 'useContext')"
|
|
165
|
+
|
|
166
|
+
**Symptoms:**
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
TypeError: Cannot read properties of null (reading 'useContext')
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Causes:**
|
|
173
|
+
- Multiple React versions
|
|
174
|
+
- Provider not wrapping the component tree
|
|
175
|
+
|
|
176
|
+
**Solutions:**
|
|
177
|
+
|
|
178
|
+
Check for duplicate React:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
npm ls react
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Ensure providers are in a Client Component layout:
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
// app/providers.tsx
|
|
188
|
+
'use client';
|
|
189
|
+
|
|
190
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
191
|
+
return <ThemeProvider>{children}</ThemeProvider>;
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### "NEXT_REDIRECT" error in try/catch
|
|
196
|
+
|
|
197
|
+
**Symptoms:**
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
Error: NEXT_REDIRECT
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Causes:**
|
|
204
|
+
- `redirect()` throws an error internally — catching it prevents the redirect
|
|
205
|
+
|
|
206
|
+
**Solutions:**
|
|
207
|
+
|
|
208
|
+
Don't wrap `redirect()` in try/catch, or re-throw:
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
import { redirect } from 'next/navigation';
|
|
212
|
+
import { isRedirectError } from 'next/dist/client/components/redirect';
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
// some logic
|
|
216
|
+
redirect('/dashboard');
|
|
217
|
+
} catch (error) {
|
|
218
|
+
if (isRedirectError(error)) throw error;
|
|
219
|
+
// handle other errors
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Caching issues
|
|
224
|
+
|
|
225
|
+
### Data not updating after mutation
|
|
226
|
+
|
|
227
|
+
- Use `revalidatePath()` or `revalidateTag()` after mutations
|
|
228
|
+
- Check if `dynamic = 'force-dynamic'` is needed
|
|
229
|
+
- See `caching-revalidation.md` for details
|
|
230
|
+
|
|
231
|
+
### Page shows stale content
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
// Force fresh data
|
|
235
|
+
export const dynamic = 'force-dynamic';
|
|
236
|
+
export const revalidate = 0;
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Debugging
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
# Verbose build output
|
|
243
|
+
NEXT_DEBUG_LOGGING=1 npx next build
|
|
244
|
+
|
|
245
|
+
# Check bundle sizes
|
|
246
|
+
npx next build --debug
|
|
247
|
+
|
|
248
|
+
# Analyze bundle
|
|
249
|
+
npm install @next/bundle-analyzer
|
|
250
|
+
ANALYZE=true npx next build
|
|
251
|
+
```
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Embedded Docs Reference
|
|
2
|
+
|
|
3
|
+
Use local project sources first to match the installed Next.js version and app configuration.
|
|
4
|
+
|
|
5
|
+
## Why local-first
|
|
6
|
+
|
|
7
|
+
- Next.js behavior changes across major releases.
|
|
8
|
+
- Repository config (`next.config.*`, app structure, runtime settings) is authoritative.
|
|
9
|
+
|
|
10
|
+
## Lookup workflow
|
|
11
|
+
|
|
12
|
+
1. Check installed version and environment:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npx next --version
|
|
16
|
+
cat node_modules/next/package.json | rg '"version"'
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
2. Inspect local config and scripts:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
ls next.config.* 2>/dev/null
|
|
23
|
+
cat package.json | rg -n '"next"|\"dev\"|\"build\"|\"start\"' -n
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
3. Inspect app router structure and route files:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
rg --files app | head -n 50
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
4. Validate runtime/build behavior:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx next info
|
|
36
|
+
npx next build
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Use this when
|
|
40
|
+
|
|
41
|
+
- Caching, dynamic rendering, or route behavior is unclear
|
|
42
|
+
- Build output does not match expectations
|
|
43
|
+
- Team-level conventions diverge from generic docs examples
|