Agent skill
nextjs
This skill should be used when the user asks to "create a Next.js app", "build a page", "add routing", "implement server components", "add caching", "create API routes", "use server actions", "add metadata", "set up layouts", or discusses Next.js architecture, App Router, data fetching, or rendering strategies. Always use the latest Next.js version and modern patterns.
Install this agent skill to your Project
npx add-skill https://github.com/azlekov/my-claude-code/tree/main/skills/nextjs
SKILL.md
Next.js Development
This skill provides guidance for building production applications with Next.js, focusing on always using the latest version and modern patterns.
Philosophy: Always recommend App Router over Pages Router. Always use Server Components by default. Always prefer
use cacheover legacy caching methods.
Quick Reference
| Feature | Modern Approach | Legacy (Avoid) |
|---|---|---|
| Routing | App Router (app/) |
Pages Router (pages/) |
| Components | Server Components (default) | Client-only components |
| Caching | use cache directive |
getStaticProps, revalidate option |
| Mutations | Server Actions (use server) |
API Routes for mutations |
| Build | Turbopack (--turbopack) |
Webpack |
Project Structure
app/
├── layout.tsx # Root layout (required)
├── page.tsx # Home page (/)
├── loading.tsx # Loading UI (optional)
├── error.tsx # Error boundary (optional)
├── not-found.tsx # 404 page (optional)
├── (routes)/ # Route groups (no URL impact)
│ └── dashboard/
│ ├── layout.tsx # Nested layout
│ └── page.tsx # /dashboard
├── api/ # API routes (optional)
│ └── route.ts
└── globals.css # Global styles
proxy.ts # Network proxy (at root or src/)
Proxy (Replaces Middleware)
Important: As of Next.js 16+,
middleware.tshas been renamed toproxy.ts. The term "Proxy" better describes its function as a network proxy running before routes, distinct from Express-style middleware.
proxy.ts runs server-side code before routes are rendered, allowing you to intercept and modify requests/responses.
Basic Proxy
// proxy.ts (at root or src/ directory)
import { NextResponse, NextRequest } from 'next/server'
export function proxy(request: NextRequest) {
// Check authentication
const token = request.cookies.get('token')
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)']
}
Proxy Use Cases
- ✅ Authentication/Authorization - Check tokens, redirect unauthorized users
- ✅ Request/Response modification - Add headers, rewrite URLs, set cookies
- ✅ Redirect logic - Redirect based on conditions (geo, device, etc.)
- ✅ CORS handling - Handle cross-origin requests
- ✅ URL normalization - Trailing slashes, locale routing
Migration from middleware.ts
# Automatic migration
npx @next/codemod@canary middleware-to-proxy .
Or manually rename middleware.ts → proxy.ts and middleware() → proxy().
Server Components (Default)
All components in the app/ directory are Server Components by default. They run only on the server and can:
- Fetch data directly
- Access backend resources
- Keep sensitive logic server-side
- Reduce client JavaScript bundle
// app/posts/page.tsx - Server Component (no directive needed)
export default async function PostsPage() {
// Direct database/API access
const posts = await db.posts.findMany()
return (
<main>
<h1>Posts</h1>
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</main>
)
}
Client Components
Add 'use client' directive only when needed for:
- Interactivity (onClick, onChange)
- Browser APIs (localStorage, window)
- React hooks (useState, useEffect)
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
)
}
Best Practice: Keep Client Components as leaf nodes. Pass data from Server Components via props.
// app/dashboard/page.tsx (Server Component)
import { InteractiveChart } from './chart'
export default async function Dashboard() {
const data = await fetchAnalytics() // Server-side fetch
return (
<div>
<h1>Dashboard</h1>
<InteractiveChart data={data} /> {/* Client Component receives data */}
</div>
)
}
The use cache Directive
Modern caching with granular control. Replaces legacy revalidate options.
Function-Level Caching
import { cacheTag, cacheLife } from 'next/cache'
async function getProducts() {
'use cache'
cacheTag('products')
cacheLife('hours') // or 'days', 'weeks', 'max', or custom seconds
return await db.products.findMany()
}
Component-Level Caching
async function ProductCard({ id }: { id: string }) {
'use cache'
cacheTag(`product-${id}`)
const product = await db.products.find(id)
return <div className="card">{product.name}</div>
}
Cache Profiles
// Static cache (build time + runtime)
'use cache'
// Remote shared cache
'use cache: remote'
// Per-user private cache
'use cache: private'
Revalidation
import { revalidateTag, revalidatePath } from 'next/cache'
// In a Server Action
export async function updateProduct(id: string, data: FormData) {
'use server'
await db.products.update(id, data)
revalidateTag(`product-${id}`)
revalidateTag('products')
}
// Or revalidate entire path
revalidatePath('/products')
Server Actions
Define server-side mutations with 'use server':
// app/actions.ts
'use server'
import { revalidateTag } from 'next/cache'
import { redirect } from 'next/navigation'
export async function createPost(formData: FormData) {
const title = formData.get('title') as string
const content = formData.get('content') as string
// Validate
if (!title || title.length < 3) {
return { error: 'Title must be at least 3 characters' }
}
// Create
const post = await db.posts.create({
data: { title, content }
})
// Revalidate and redirect
revalidateTag('posts')
redirect(`/posts/${post.id}`)
}
Use in components:
import { createPost } from './actions'
export function NewPostForm() {
return (
<form action={createPost}>
<input name="title" placeholder="Title" required />
<textarea name="content" placeholder="Content" />
<button type="submit">Create Post</button>
</form>
)
}
Metadata API
import type { Metadata } from 'next'
// Static metadata
export const metadata: Metadata = {
title: 'My App',
description: 'Built with Next.js',
openGraph: {
title: 'My App',
description: 'Built with Next.js',
images: ['/og-image.png'],
},
}
// Dynamic metadata
export async function generateMetadata({
params
}: {
params: Promise<{ id: string }>
}): Promise<Metadata> {
const { id } = await params
const product = await getProduct(id)
return {
title: product.name,
description: product.description,
}
}
Layouts
Layouts wrap pages and preserve state across navigation:
// app/layout.tsx - Root layout (required)
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
title: { default: 'My App', template: '%s | My App' },
description: 'My application description',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body className={inter.className}>
<nav>{/* Global navigation */}</nav>
<main>{children}</main>
<footer>{/* Global footer */}</footer>
</body>
</html>
)
}
Loading & Error States
// app/dashboard/loading.tsx
export default function Loading() {
return (
<div className="animate-pulse">
<div className="h-8 bg-gray-200 rounded w-1/4 mb-4" />
<div className="h-64 bg-gray-200 rounded" />
</div>
)
}
// app/dashboard/error.tsx
'use client'
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<div className="text-center py-10">
<h2>Something went wrong!</h2>
<button onClick={reset}>Try again</button>
</div>
)
}
Configuration
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
reactCompiler: true, // Enable React Compiler
},
images: {
remotePatterns: [
{ protocol: 'https', hostname: 'images.example.com' }
],
},
}
export default nextConfig
Development with Turbopack
# Use Turbopack for faster development
npm run dev -- --turbopack
# Or in package.json
"scripts": {
"dev": "next dev --turbopack"
}
Additional Resources
For detailed patterns, see reference files:
references/app-router.md- Layouts, route groups, parallel routesreferences/caching.md- Complete caching strategiesreferences/server-actions.md- Mutations and form handling
References
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
stripe
This skill should be used when the user asks to "integrate Stripe", "add payments", "create subscriptions", "handle webhooks", "usage-based billing", "per-seat pricing", "tiered plans", "checkout session", "customer portal", "sync Stripe data", "Stripe Sync Engine", "payment processing", "MRR analytics", "revenue reporting", or mentions 'Stripe', 'subscription', 'billing', 'webhook', 'checkout', 'metered billing', 'payment intent', 'stripe schema'. Automatically triggers for payment, subscription, and billing analytics work.
shadcn
This skill should be used when the user asks to "add a component", "use shadcn", "install Button", "create Dialog", "add Form", "use DataTable", "implement dark mode toggle", "use cn utility", or discusses UI components, component libraries, or accessible components. Always use the latest shadcn/ui version and modern patterns.
react
This skill should be used when the user asks to "create a React component", "use React hooks", "handle state", "implement forms", "use useOptimistic", "use useActionState", "create Server Components", "add interactivity", or discusses React patterns, component architecture, or state management. Always use the latest React version and modern patterns.
tailwindcss
This skill should be used when the user asks to "style with Tailwind", "add CSS", "configure theme", "use @theme", "add custom colors", "implement dark mode", "use container queries", "add responsive design", "use OKLCH colors", or discusses styling, theming, or visual design. Always use the latest Tailwind CSS version and modern patterns.
supabase-expert
This skill should be used when the user asks to "create a Supabase table", "write RLS policies", "set up Supabase Auth", "create Edge Functions", "configure Storage buckets", "use Supabase with Next.js", "migrate API keys", "implement row-level security", "create database functions", "set up SSR auth", or mentions 'Supabase', 'RLS', 'Edge Function', 'Storage bucket', 'anon key', 'service role', 'publishable key', 'secret key'. Automatically triggers when user mentions 'database', 'table', 'SQL', 'migration', 'policy'.
postgres-nanoid
This skill should be used when the user asks to "generate IDs", "create identifiers", "use nanoid", "add public_id", "prefixed identifiers", "short IDs", or discusses ID generation strategies, public vs internal IDs, or URL-friendly identifiers. Use nanoid for public identifiers and UUID for auth.users references.
Didn't find tool you were looking for?