@codihaus/claude-skills 1.0.0 → 1.2.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.
@@ -0,0 +1,654 @@
1
+ # Next.js
2
+
3
+ > React meta-framework with SSR, App Router, and Server Components
4
+
5
+ ## Overview
6
+
7
+ **What it is:**
8
+ - Full-stack React framework
9
+ - Server-side rendering (SSR) and static generation (SSG)
10
+ - App Router with file-based routing (app/)
11
+ - React Server Components by default
12
+ - API routes and Server Actions
13
+ - Built-in image optimization
14
+
15
+ **When to use:**
16
+ - React applications needing SEO (SSR)
17
+ - Full-stack apps (frontend + API)
18
+ - Projects wanting convention over configuration
19
+ - Teams familiar with React ecosystem
20
+
21
+ **Key concepts:**
22
+ - **App Router** = File-based routes in `app/`
23
+ - **Server Components** = Default, run on server
24
+ - **Client Components** = Use `"use client"` directive
25
+ - **Server Actions** = Form handling with `"use server"`
26
+ - **Route Handlers** = API endpoints in `app/api/`
27
+ - **Middleware** = Edge functions for request interception
28
+ - **Layouts** = Shared UI in `layout.tsx`
29
+
30
+ ## Best Practices
31
+
32
+ ### Project Structure
33
+
34
+ ```
35
+ nextjs-app/
36
+ ├── app/ # App Router (Next.js 13+)
37
+ │ ├── (auth)/ # Route groups (no URL impact)
38
+ │ │ ├── login/
39
+ │ │ └── register/
40
+ │ ├── api/ # API routes
41
+ │ │ └── [route]/route.ts
42
+ │ ├── dashboard/
43
+ │ │ ├── page.tsx
44
+ │ │ └── layout.tsx
45
+ │ ├── layout.tsx # Root layout
46
+ │ ├── page.tsx # Home page
47
+ │ └── globals.css
48
+ ├── components/ # React components
49
+ │ ├── ui/ # UI primitives
50
+ │ ├── forms/ # Form components
51
+ │ └── common/ # Shared components
52
+ ├── lib/ # Utilities and configs
53
+ │ ├── actions/ # Server Actions
54
+ │ ├── api.ts # API client
55
+ │ ├── auth.ts # Auth utilities
56
+ │ └── utils.ts # Helpers
57
+ ├── hooks/ # Custom React hooks
58
+ ├── types/ # TypeScript types
59
+ ├── public/ # Static assets
60
+ └── middleware.ts # Edge middleware
61
+ ```
62
+
63
+ ### Data Fetching
64
+
65
+ ```
66
+ DO:
67
+ ✓ Fetch in Server Components by default (no useEffect)
68
+ ✓ Use async/await directly in components
69
+ ✓ Use Server Actions for mutations
70
+ ✓ Use fetch with caching options for external APIs
71
+ ✓ Use React cache() for request deduplication
72
+
73
+ DON'T:
74
+ ✗ Use useEffect for initial data in Server Components
75
+ ✗ Forget revalidation strategy (stale data)
76
+ ✗ Mix Server and Client data fetching unnecessarily
77
+ ✗ Ignore loading.tsx and error.tsx
78
+ ```
79
+
80
+ ### Server vs Client Components
81
+
82
+ ```
83
+ Server Components (default):
84
+ ✓ Data fetching
85
+ ✓ Accessing backend resources
86
+ ✓ Sensitive logic (API keys, tokens)
87
+ ✓ Large dependencies
88
+
89
+ Client Components ("use client"):
90
+ ✓ Interactivity (onClick, onChange)
91
+ ✓ State (useState, useReducer)
92
+ ✓ Effects (useEffect)
93
+ ✓ Browser APIs (localStorage, etc.)
94
+ ✓ Custom hooks with state
95
+ ```
96
+
97
+ ### Server Actions
98
+
99
+ ```
100
+ DO:
101
+ ✓ Define with "use server" directive
102
+ ✓ Use for form submissions and mutations
103
+ ✓ Validate input with Zod
104
+ ✓ Return typed responses
105
+ ✓ Use revalidatePath/revalidateTag after mutations
106
+
107
+ DON'T:
108
+ ✗ Use for read operations (use Server Components)
109
+ ✗ Expose sensitive data in return values
110
+ ✗ Skip input validation
111
+ ✗ Forget error handling
112
+ ```
113
+
114
+ ### API Routes (Route Handlers)
115
+
116
+ ```
117
+ DO:
118
+ ✓ Use app/api/ for REST endpoints
119
+ ✓ Export named functions (GET, POST, PUT, DELETE)
120
+ ✓ Use NextRequest/NextResponse
121
+ ✓ Validate request body
122
+ ✓ Return proper status codes
123
+
124
+ DON'T:
125
+ ✗ Use for data consumed by own app (use Server Components)
126
+ ✗ Skip authentication checks
127
+ ✗ Return sensitive data without authorization
128
+ ```
129
+
130
+ ### State Management
131
+
132
+ ```
133
+ DO:
134
+ ✓ Lift state up for shared client state
135
+ ✓ Use React Context for global client state
136
+ ✓ Use Server Components to avoid client state when possible
137
+ ✓ Use URL state (searchParams) for shareable state
138
+ ✓ Consider Zustand for complex client state
139
+
140
+ DON'T:
141
+ ✗ Use Redux by default (often overkill with Server Components)
142
+ ✗ Store server data in client state (refetch instead)
143
+ ✗ Forget URL state for filters/pagination
144
+ ```
145
+
146
+ ## Anti-Patterns
147
+
148
+ | Anti-Pattern | Problem | Better Approach |
149
+ |--------------|---------|-----------------|
150
+ | useEffect for data | Waterfalls, spinners | Fetch in Server Component |
151
+ | "use client" everywhere | Loses RSC benefits | Only where needed |
152
+ | Huge Client Components | Large bundle | Split server/client |
153
+ | Fetching in layout.tsx | Blocks nested routes | Fetch in page.tsx or parallel |
154
+ | No loading.tsx | No loading UI | Add loading.tsx per route |
155
+ | Ignoring cache | Redundant fetches | Use fetch cache options |
156
+ | API routes for own data | Extra roundtrip | Server Components/Actions |
157
+
158
+ ## For /dev-specs
159
+
160
+ ### Page Specification
161
+
162
+ ```markdown
163
+ ## Page: /products/[id]
164
+
165
+ ### Route
166
+ - Path: `/products/:id`
167
+ - File: `app/products/[id]/page.tsx`
168
+ - Layout: Uses `app/products/layout.tsx`
169
+
170
+ ### Data Requirements
171
+ - Product details (Server Component fetch)
172
+ - Related products (parallel fetch)
173
+
174
+ ### Components
175
+ | Component | Type | Purpose |
176
+ |-----------|------|---------|
177
+ | ProductGallery | Client | Image carousel with interactions |
178
+ | ProductInfo | Server | Static product details |
179
+ | AddToCart | Client | Cart interaction with state |
180
+
181
+ ### Loading State
182
+ - `loading.tsx` with product skeleton
183
+
184
+ ### SEO
185
+ - generateMetadata for dynamic title/description
186
+ - Open Graph images
187
+ ```
188
+
189
+ ### Server Action Specification
190
+
191
+ ```markdown
192
+ ## Action: addToCart
193
+
194
+ ### Purpose
195
+ Add product to user's cart
196
+
197
+ ### File
198
+ `lib/actions/cart.ts`
199
+
200
+ ### Input
201
+ ```typescript
202
+ {
203
+ productId: string,
204
+ quantity: number,
205
+ variant?: string,
206
+ }
207
+ ```
208
+
209
+ ### Validation
210
+ - Zod schema for input
211
+ - Check product exists
212
+ - Check stock availability
213
+
214
+ ### Side Effects
215
+ - Update cart in database
216
+ - revalidatePath('/cart')
217
+
218
+ ### Returns
219
+ ```typescript
220
+ { success: true, cartId: string }
221
+ | { success: false, error: string }
222
+ ```
223
+ ```
224
+
225
+ ### API Route Specification
226
+
227
+ ```markdown
228
+ ## API: POST /api/webhooks/stripe
229
+
230
+ ### Purpose
231
+ Handle Stripe webhook events
232
+
233
+ ### File
234
+ `app/api/webhooks/stripe/route.ts`
235
+
236
+ ### Authentication
237
+ - Verify Stripe signature
238
+ - No user auth (webhook)
239
+
240
+ ### Events Handled
241
+ | Event | Action |
242
+ |-------|--------|
243
+ | checkout.session.completed | Create order |
244
+ | payment_intent.failed | Update order status |
245
+
246
+ ### Response
247
+ - 200: Event processed
248
+ - 400: Invalid payload
249
+ - 401: Invalid signature
250
+ ```
251
+
252
+ ## For /dev-coding
253
+
254
+ ### Server Component with Data Fetching
255
+
256
+ ```typescript
257
+ // app/products/[id]/page.tsx
258
+ import { notFound } from 'next/navigation';
259
+ import { getProduct, getRelatedProducts } from '@/lib/api';
260
+
261
+ interface Props {
262
+ params: Promise<{ id: string }>;
263
+ }
264
+
265
+ export async function generateMetadata({ params }: Props) {
266
+ const { id } = await params;
267
+ const product = await getProduct(id);
268
+
269
+ if (!product) return { title: 'Not Found' };
270
+
271
+ return {
272
+ title: product.name,
273
+ description: product.description,
274
+ openGraph: {
275
+ images: [product.image],
276
+ },
277
+ };
278
+ }
279
+
280
+ export default async function ProductPage({ params }: Props) {
281
+ const { id } = await params;
282
+
283
+ // Parallel fetching
284
+ const [product, related] = await Promise.all([
285
+ getProduct(id),
286
+ getRelatedProducts(id),
287
+ ]);
288
+
289
+ if (!product) notFound();
290
+
291
+ return (
292
+ <div>
293
+ <h1>{product.name}</h1>
294
+ <p>{product.price}</p>
295
+ <AddToCartButton productId={id} />
296
+ <RelatedProducts products={related} />
297
+ </div>
298
+ );
299
+ }
300
+ ```
301
+
302
+ ### Client Component
303
+
304
+ ```typescript
305
+ // components/AddToCartButton.tsx
306
+ "use client";
307
+
308
+ import { useState, useTransition } from 'react';
309
+ import { addToCart } from '@/lib/actions/cart';
310
+
311
+ interface Props {
312
+ productId: string;
313
+ }
314
+
315
+ export function AddToCartButton({ productId }: Props) {
316
+ const [isPending, startTransition] = useTransition();
317
+ const [error, setError] = useState<string | null>(null);
318
+
319
+ const handleClick = () => {
320
+ setError(null);
321
+ startTransition(async () => {
322
+ const result = await addToCart({ productId, quantity: 1 });
323
+ if (!result.success) {
324
+ setError(result.error);
325
+ }
326
+ });
327
+ };
328
+
329
+ return (
330
+ <>
331
+ <button onClick={handleClick} disabled={isPending}>
332
+ {isPending ? 'Adding...' : 'Add to Cart'}
333
+ </button>
334
+ {error && <p className="text-red-500">{error}</p>}
335
+ </>
336
+ );
337
+ }
338
+ ```
339
+
340
+ ### Server Action
341
+
342
+ ```typescript
343
+ // lib/actions/cart.ts
344
+ "use server";
345
+
346
+ import { z } from 'zod';
347
+ import { revalidatePath } from 'next/cache';
348
+ import { auth } from '@/lib/auth';
349
+ import { db } from '@/lib/db';
350
+
351
+ const addToCartSchema = z.object({
352
+ productId: z.string().min(1),
353
+ quantity: z.number().int().positive(),
354
+ });
355
+
356
+ type AddToCartInput = z.infer<typeof addToCartSchema>;
357
+ type AddToCartResult =
358
+ | { success: true; cartId: string }
359
+ | { success: false; error: string };
360
+
361
+ export async function addToCart(input: AddToCartInput): Promise<AddToCartResult> {
362
+ // Validate input
363
+ const parsed = addToCartSchema.safeParse(input);
364
+ if (!parsed.success) {
365
+ return { success: false, error: 'Invalid input' };
366
+ }
367
+
368
+ // Check auth
369
+ const session = await auth();
370
+ if (!session?.user) {
371
+ return { success: false, error: 'Please sign in' };
372
+ }
373
+
374
+ try {
375
+ // Add to cart
376
+ const cart = await db.cart.upsert({
377
+ where: { userId: session.user.id },
378
+ create: {
379
+ userId: session.user.id,
380
+ items: { create: parsed.data },
381
+ },
382
+ update: {
383
+ items: { create: parsed.data },
384
+ },
385
+ });
386
+
387
+ revalidatePath('/cart');
388
+ return { success: true, cartId: cart.id };
389
+ } catch (error) {
390
+ return { success: false, error: 'Failed to add to cart' };
391
+ }
392
+ }
393
+ ```
394
+
395
+ ### API Route Handler
396
+
397
+ ```typescript
398
+ // app/api/products/route.ts
399
+ import { NextRequest, NextResponse } from 'next/server';
400
+ import { z } from 'zod';
401
+ import { auth } from '@/lib/auth';
402
+ import { db } from '@/lib/db';
403
+
404
+ const createProductSchema = z.object({
405
+ name: z.string().min(1),
406
+ price: z.number().positive(),
407
+ description: z.string().optional(),
408
+ });
409
+
410
+ export async function GET(request: NextRequest) {
411
+ const searchParams = request.nextUrl.searchParams;
412
+ const limit = parseInt(searchParams.get('limit') || '10');
413
+ const offset = parseInt(searchParams.get('offset') || '0');
414
+
415
+ const products = await db.product.findMany({
416
+ take: limit,
417
+ skip: offset,
418
+ orderBy: { createdAt: 'desc' },
419
+ });
420
+
421
+ return NextResponse.json({ data: products });
422
+ }
423
+
424
+ export async function POST(request: NextRequest) {
425
+ // Check auth
426
+ const session = await auth();
427
+ if (!session?.user?.isAdmin) {
428
+ return NextResponse.json(
429
+ { error: 'Unauthorized' },
430
+ { status: 401 }
431
+ );
432
+ }
433
+
434
+ // Validate body
435
+ const body = await request.json();
436
+ const parsed = createProductSchema.safeParse(body);
437
+
438
+ if (!parsed.success) {
439
+ return NextResponse.json(
440
+ { error: 'Invalid input', details: parsed.error.flatten() },
441
+ { status: 400 }
442
+ );
443
+ }
444
+
445
+ // Create product
446
+ const product = await db.product.create({
447
+ data: parsed.data,
448
+ });
449
+
450
+ return NextResponse.json({ data: product }, { status: 201 });
451
+ }
452
+ ```
453
+
454
+ ### Middleware (Auth)
455
+
456
+ ```typescript
457
+ // middleware.ts
458
+ import { NextResponse } from 'next/server';
459
+ import type { NextRequest } from 'next/server';
460
+ import { auth } from '@/lib/auth';
461
+
462
+ export async function middleware(request: NextRequest) {
463
+ const session = await auth();
464
+
465
+ // Protect dashboard routes
466
+ if (request.nextUrl.pathname.startsWith('/dashboard')) {
467
+ if (!session) {
468
+ return NextResponse.redirect(new URL('/login', request.url));
469
+ }
470
+ }
471
+
472
+ // Protect admin routes
473
+ if (request.nextUrl.pathname.startsWith('/admin')) {
474
+ if (!session?.user?.isAdmin) {
475
+ return NextResponse.redirect(new URL('/unauthorized', request.url));
476
+ }
477
+ }
478
+
479
+ return NextResponse.next();
480
+ }
481
+
482
+ export const config = {
483
+ matcher: ['/dashboard/:path*', '/admin/:path*'],
484
+ };
485
+ ```
486
+
487
+ ### Loading and Error States
488
+
489
+ ```typescript
490
+ // app/products/[id]/loading.tsx
491
+ export default function Loading() {
492
+ return (
493
+ <div className="animate-pulse">
494
+ <div className="h-8 bg-gray-200 rounded w-1/4 mb-4" />
495
+ <div className="h-4 bg-gray-200 rounded w-1/2 mb-2" />
496
+ <div className="h-64 bg-gray-200 rounded" />
497
+ </div>
498
+ );
499
+ }
500
+
501
+ // app/products/[id]/error.tsx
502
+ "use client";
503
+
504
+ interface Props {
505
+ error: Error & { digest?: string };
506
+ reset: () => void;
507
+ }
508
+
509
+ export default function Error({ error, reset }: Props) {
510
+ return (
511
+ <div className="text-center py-10">
512
+ <h2>Something went wrong!</h2>
513
+ <p className="text-gray-500">{error.message}</p>
514
+ <button onClick={reset} className="mt-4 btn">
515
+ Try again
516
+ </button>
517
+ </div>
518
+ );
519
+ }
520
+ ```
521
+
522
+ ## For /dev-review
523
+
524
+ ### Server Component Checklist
525
+
526
+ - [ ] Data fetching in Server Components (not useEffect)
527
+ - [ ] Parallel fetching with Promise.all
528
+ - [ ] Proper loading.tsx for each route segment
529
+ - [ ] error.tsx for error boundaries
530
+ - [ ] not-found.tsx for 404 handling
531
+ - [ ] generateMetadata for SEO
532
+
533
+ ### Client Component Checklist
534
+
535
+ - [ ] "use client" only where needed
536
+ - [ ] Minimal bundle size (no large deps)
537
+ - [ ] useTransition for non-blocking updates
538
+ - [ ] Proper error handling in actions
539
+ - [ ] Loading states during transitions
540
+
541
+ ### Security Checklist
542
+
543
+ - [ ] Server Actions validate input
544
+ - [ ] API routes check authorization
545
+ - [ ] Middleware protects routes
546
+ - [ ] No secrets exposed to client
547
+ - [ ] CSRF protection on mutations
548
+ - [ ] Rate limiting on API routes
549
+
550
+ ### Performance Checklist
551
+
552
+ - [ ] Images use next/image
553
+ - [ ] Dynamic imports for heavy components
554
+ - [ ] Route segments have loading.tsx
555
+ - [ ] Proper cache headers on fetch
556
+ - [ ] Static generation where possible
557
+
558
+ ### Common Mistakes
559
+
560
+ | Mistake | Impact | Fix |
561
+ |---------|--------|-----|
562
+ | useEffect for data | Waterfalls | Server Component fetch |
563
+ | "use client" on page | Loses SSR | Keep pages as Server Components |
564
+ | No loading.tsx | No loading UI | Add per route segment |
565
+ | Fetching in layout | Blocks children | Fetch in page or parallel |
566
+ | Large Client Components | Big bundle | Split and lazy load |
567
+ | No error boundary | Crashes page | Add error.tsx |
568
+
569
+ ## Integration
570
+
571
+ ### With Directus
572
+
573
+ ```typescript
574
+ // lib/directus.ts
575
+ import { createDirectus, rest, authentication } from '@directus/sdk';
576
+
577
+ const directus = createDirectus<Schema>(process.env.DIRECTUS_URL!)
578
+ .with(rest())
579
+ .with(authentication());
580
+
581
+ export default directus;
582
+
583
+ // lib/api/products.ts
584
+ import directus from '@/lib/directus';
585
+ import { readItems, readItem } from '@directus/sdk';
586
+
587
+ export async function getProducts() {
588
+ return directus.request(
589
+ readItems('products', {
590
+ filter: { status: { _eq: 'published' } },
591
+ sort: ['-date_created'],
592
+ })
593
+ );
594
+ }
595
+
596
+ export async function getProduct(id: string) {
597
+ return directus.request(readItem('products', id));
598
+ }
599
+ ```
600
+
601
+ ### With NextAuth.js
602
+
603
+ ```typescript
604
+ // lib/auth.ts
605
+ import NextAuth from 'next-auth';
606
+ import GitHub from 'next-auth/providers/github';
607
+
608
+ export const { handlers, auth, signIn, signOut } = NextAuth({
609
+ providers: [GitHub],
610
+ callbacks: {
611
+ session({ session, token }) {
612
+ if (token.sub) session.user.id = token.sub;
613
+ return session;
614
+ },
615
+ },
616
+ });
617
+
618
+ // app/api/auth/[...nextauth]/route.ts
619
+ import { handlers } from '@/lib/auth';
620
+ export const { GET, POST } = handlers;
621
+ ```
622
+
623
+ ### With Prisma
624
+
625
+ ```typescript
626
+ // lib/db.ts
627
+ import { PrismaClient } from '@prisma/client';
628
+
629
+ const globalForPrisma = globalThis as unknown as {
630
+ prisma: PrismaClient | undefined;
631
+ };
632
+
633
+ export const db = globalForPrisma.prisma ?? new PrismaClient();
634
+
635
+ if (process.env.NODE_ENV !== 'production') {
636
+ globalForPrisma.prisma = db;
637
+ }
638
+ ```
639
+
640
+ ## Resources
641
+
642
+ ### Official Docs
643
+ - Docs: https://nextjs.org/docs
644
+ - App Router: https://nextjs.org/docs/app
645
+ - Learn: https://nextjs.org/learn
646
+
647
+ ### Context7 Queries
648
+
649
+ ```
650
+ Query: "Next.js 15 App Router data fetching"
651
+ Query: "Next.js Server Actions best practices"
652
+ Query: "Next.js middleware authentication"
653
+ Query: "Next.js Server Components patterns"
654
+ ```