nextjs-patterns
SKILL.md
Next.js Patterns
Modern Next.js with App Router, Server Components, and production patterns.
App Router Structure
app/
layout.tsx # Root layout (wraps all pages)
page.tsx # Home page (/)
loading.tsx # Loading UI (automatic Suspense)
error.tsx # Error boundary
not-found.tsx # 404 page
api/
route.ts # API route (/api)
dashboard/
layout.tsx # Nested layout
page.tsx # Dashboard page (/dashboard)
[id]/
page.tsx # Dynamic route (/dashboard/123)
Server Components (default)
// app/users/page.tsx — runs on server, no client JS
async function UsersPage() {
const users = await db.users.findMany(); // Direct DB access
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}
Client Components
'use client'; // Only add when you need interactivity
import { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
Data Fetching
Server Component (preferred)
// Fetch in the component — no useEffect needed
async function ProductPage({ params }: { params: { id: string } }) {
const product = await fetch(`https://api.example.com/products/${params.id}`, {
next: { revalidate: 3600 }, // Cache for 1 hour
}).then(r => r.json());
return <Product data={product} />;
}
Server Actions (mutations)
// app/actions.ts
'use server';
export async function createUser(formData: FormData) {
const name = formData.get('name') as string;
await db.users.create({ data: { name } });
revalidatePath('/users');
}
// In component:
<form action={createUser}>
<input name="name" />
<button type="submit">Create</button>
</form>
API Routes
// app/api/users/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
const users = await db.users.findMany();
return NextResponse.json(users);
}
export async function POST(request: Request) {
const body = await request.json();
const user = await db.users.create({ data: body });
return NextResponse.json(user, { status: 201 });
}
Middleware
// middleware.ts (root level)
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const token = request.cookies.get('auth-token');
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = { matcher: ['/dashboard/:path*'] };
Commands
# Dev server
npx next dev
# Production build
npx next build
# Start production server
npx next start
# Lint
npx next lint
# Analyze bundle
ANALYZE=true npx next build
Notes
- Default to Server Components. Only add
'use client'when you needuseState,useEffect, or browser APIs. - Server Components can
awaitdirectly — no loading states needed (useloading.tsxfor automatic Suspense). fetchin Server Components is automatically deduped and cached.- Use
revalidatePath()orrevalidateTag()to invalidate cache after mutations. - Keep Client Components small and push them to leaf nodes of the component tree.
Weekly Installs
2
Repository
thinkfleetai/th…t-engineFirst Seen
13 days ago
Security Audits
Installed on
opencode2
claude-code2
github-copilot2
codex2
kimi-cli2
gemini-cli2