.agent/skills/nextjs-expert/SKILL.md
Next.js framework expert specializing in App Router, Server Components, performance optimization, data fetching patterns, and full-stack development. Use this skill for Next.js routing issues, hydration errors, build problems, or deployment challenges.
npx skillsauth add ripgraphics/authorsinfo nextjs-expertInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are an expert in Next.js 13-15 with deep knowledge of App Router, Server Components, data fetching patterns, performance optimization, and deployment strategies.
If the issue is specifically about:
# Detect Next.js version and router type
npx next --version 2>/dev/null || node -e "console.log(require('./package.json').dependencies?.next || 'Not found')" 2>/dev/null
# Check router architecture
if [ -d "app" ] && [ -d "pages" ]; then echo "Mixed Router Setup - Both App and Pages"
elif [ -d "app" ]; then echo "App Router"
elif [ -d "pages" ]; then echo "Pages Router"
else echo "No router directories found"
fi
# Check deployment configuration
if [ -f "vercel.json" ]; then echo "Vercel deployment config found"
elif [ -f "Dockerfile" ]; then echo "Docker deployment"
elif [ -f "netlify.toml" ]; then echo "Netlify deployment"
else echo "No deployment config detected"
fi
# Check for performance features
grep -q "next/image" pages/**/*.js pages/**/*.tsx app/**/*.js app/**/*.tsx 2>/dev/null && echo "Next.js Image optimization used" || echo "No Image optimization detected"
grep -q "generateStaticParams\|getStaticPaths" pages/**/*.js pages/**/*.tsx app/**/*.js app/**/*.tsx 2>/dev/null && echo "Static generation configured" || echo "No static generation detected"
Common Issues:
Diagnosis:
# Check for hook usage in potential Server Components
grep -r "useState\|useEffect" app/ --include="*.js" --include="*.jsx" --include="*.ts" --include="*.tsx" | grep -v "use client"
# Find browser API usage
grep -r "window\|document\|localStorage\|sessionStorage" app/ --include="*.js" --include="*.jsx" --include="*.ts" --include="*.tsx"
# Check Client Component boundaries
grep -r "use client" app/ --include="*.js" --include="*.jsx" --include="*.ts" --include="*.tsx"
# Analyze bundle size
npx @next/bundle-analyzer 2>/dev/null || echo "Bundle analyzer not configured"
Prioritized Fixes:
typeof window !== 'undefined' checksValidation:
npm run build && npm run start
# Check for hydration errors in browser console
# Verify bundle size reduction with next/bundle-analyzer
Resources:
Common Issues:
Diagnosis:
# Find data fetching patterns
grep -r "fetch(" app/ --include="*.js" --include="*.jsx" --include="*.ts" --include="*.tsx"
# Check for cookies usage
grep -r "cookies()" app/ --include="*.js" --include="*.jsx" --include="*.ts" --include="*.tsx"
# Look for caching configuration
grep -r "cache:\|revalidate:" app/ --include="*.js" --include="*.jsx" --include="*.ts" --include="*.tsx"
# Check for generateStaticParams
grep -r "generateStaticParams" app/ --include="*.js" --include="*.jsx" --include="*.ts" --include="*.tsx"
Prioritized Fixes:
cache: 'no-store' for dynamic data, move cookie access to Server ComponentsPromise.all() for parallel requests, implement proper revalidation strategiesValidation:
# Test caching behavior
curl -I http://localhost:3000/api/data
# Check build output for static generation
npm run build
# Verify revalidation timing
Resources:
Common Issues:
Diagnosis:
# Check dynamic route structure
find app/ -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" | grep "\[.*\]"
# Find generateStaticParams usage
grep -r "generateStaticParams" app/ --include="*.js" --include="*.jsx" --include="*.ts" --include="*.tsx"
# Check build output
npm run build 2>&1 | grep -E "(Static|Generated|Error)"
# Test dynamic routes
ls -la .next/server/app/ 2>/dev/null || echo "Build output not found"
Prioritized Fixes:
dynamicParams = true for ISR, implement proper error boundariesValidation:
# Build and check generated pages
npm run build && ls -la .next/server/app/
# Test dynamic routes manually
curl http://localhost:3000/your-dynamic-route
Resources:
Common Issues:
Diagnosis:
# Check Image optimization usage
grep -r "next/image" app/ pages/ --include="*.js" --include="*.jsx" --include="*.ts" --include="*.tsx"
# Find large images without optimization
find public/ -name "*.jpg" -o -name "*.jpeg" -o -name "*.png" -o -name "*.webp" | xargs ls -lh 2>/dev/null
# Check font optimization
grep -r "next/font" app/ pages/ --include="*.js" --include="*.jsx" --include="*.ts" --include="*.tsx"
# Analyze bundle size
npm run build 2>&1 | grep -E "(First Load JS|Size)"
Prioritized Fixes:
priority to above-fold imagesValidation:
# Run Lighthouse audit
npx lighthouse http://localhost:3000 --chrome-flags="--headless" 2>/dev/null || echo "Lighthouse not available"
# Check Core Web Vitals
# Verify WebP/AVIF format serving in Network tab
Resources:
Common Issues:
Diagnosis:
# Check Route Handler structure
find app/ -name "route.js" -o -name "route.ts" | head -10
# Verify HTTP method exports
grep -r "export async function \(GET\|POST\|PUT\|DELETE\)" app/ --include="route.js" --include="route.ts"
# Check API route configuration
grep -r "export const \(runtime\|dynamic\|revalidate\)" app/ --include="route.js" --include="route.ts"
# Test API routes
ls -la app/api/ 2>/dev/null || echo "No API routes found"
Prioritized Fixes:
Validation:
# Test API endpoints
curl http://localhost:3000/api/your-route
# Check serverless function logs
npm run build && npm run start
Resources:
Common Issues:
Diagnosis:
# Check middleware configuration
[ -f "middleware.js" ] || [ -f "middleware.ts" ] && echo "Middleware found" || echo "No middleware file"
# Check matcher configuration
grep -r "config.*matcher" middleware.js middleware.ts 2>/dev/null
# Find authentication patterns
grep -r "cookies\|session\|auth" middleware.js middleware.ts app/ --include="*.js" --include="*.ts" | head -10
# Check for Node.js APIs in middleware (edge compatibility)
grep -r "fs\|path\|crypto\.randomBytes" middleware.js middleware.ts 2>/dev/null
Prioritized Fixes:
Validation:
# Test middleware execution
# Check browser Network tab for redirect chains
# Verify cookie behavior in Application tab
Resources:
Common Issues:
Diagnosis:
# Check environment variables
grep -r "process\.env\|NEXT_PUBLIC_" app/ pages/ --include="*.js" --include="*.jsx" --include="*.ts" --include="*.tsx" | head -10
# Test local build
npm run build 2>&1 | grep -E "(Error|Failed|Warning)"
# Check deployment configuration
[ -f "vercel.json" ] && echo "Vercel config found" || echo "No Vercel config"
[ -f "Dockerfile" ] && echo "Docker config found" || echo "No Docker config"
# Check for static export configuration
grep -r "output.*export" next.config.js next.config.mjs 2>/dev/null
Prioritized Fixes:
Validation:
# Test production build locally
npm run build && npm run start
# Verify environment variables load correctly
# Check deployment logs for errors
Resources:
Common Issues:
Diagnosis:
# Check for mixed router setup
[ -d "pages" ] && [ -d "app" ] && echo "Mixed router setup detected"
# Find old Pages Router patterns
grep -r "getServerSideProps\|getStaticProps\|getInitialProps" pages/ --include="*.js" --include="*.jsx" --include="*.ts" --include="*.tsx" 2>/dev/null
# Check API route migration
[ -d "pages/api" ] && [ -d "app/api" ] && echo "API routes in both locations"
# Look for layout issues
grep -r "\_app\|\_document" pages/ --include="*.js" --include="*.jsx" --include="*.ts" --include="*.tsx" 2>/dev/null
Prioritized Fixes:
Validation:
# Test migrated functionality
npm run dev
# Verify all routes work correctly
# Check for deprecated pattern warnings
Resources:
Common Issues:
Diagnosis:
# Find Server Actions
grep -r "'use server'" app/ --include="*.ts" --include="*.tsx"
# Check for revalidation usage
grep -r "revalidatePath\|revalidateTag" app/ --include="*.ts" --include="*.tsx"
# Find form submissions
grep -r "action=\|formAction" app/ --include="*.tsx" --include="*.jsx"
# Check Server Action error handling
grep -r "try.*catch" app/actions/ --include="*.ts"
Prioritized Fixes:
Server Action Pattern:
'use server'
import { revalidatePath } from 'next/cache'
import { createServerActionClientAsync } from '@/lib/supabase/client-helper'
export async function createActivity(params: CreateActivityParams) {
try {
const supabase = await createServerActionClientAsync()
// Authentication
const { data: { user }, error: authError } = await supabase.auth.getUser()
if (authError || !user) {
return { success: false, error: 'Authentication required' }
}
// Validation
if (!params.activity_type) {
return { success: false, error: 'Activity type is required' }
}
// Database operation
const { data, error } = await supabase
.from('posts')
.insert({ ...params, user_id: user.id })
.select()
.single()
if (error) {
console.error('Error creating activity:', error)
return { success: false, error: 'Failed to create activity' }
}
// Revalidate affected paths
revalidatePath('/feed')
revalidatePath(`/profile/${user.id}`)
if (params.group_id) {
revalidatePath(`/groups/${params.group_id}`)
}
return { success: true, activity: data }
} catch (error) {
console.error('Unexpected error:', error)
return { success: false, error: 'Internal server error' }
}
}
Revalidation Patterns:
// Revalidate specific page
revalidatePath('/books/[id]', 'page')
// Revalidate layout
revalidatePath('/profile/[id]', 'layout')
// Revalidate all pages under path
revalidatePath('/books')
// Revalidate by tag
revalidateTag('books')
Form Integration:
'use client'
import { useActionState } from 'react'
import { createActivity } from '@/app/actions/activities'
export function ActivityForm() {
const [state, formAction, isPending] = useActionState(createActivity, null)
return (
<form action={formAction}>
<input name="activity_type" required />
{state?.error && <p className="error">{state.error}</p>}
<button type="submit" disabled={isPending}>
{isPending ? 'Submitting...' : 'Submit'}
</button>
</form>
)
}
Validation:
# Test Server Action
# Check network tab for form submission
# Verify cache invalidation after mutation
# Test error handling with invalid data
Resources:
Common Issues:
Diagnosis:
# Find metadata usage
grep -r "export.*metadata\|generateMetadata" app/ --include="*.ts" --include="*.tsx"
# Check for dynamic metadata
grep -r "generateMetadata.*params" app/ --include="*.ts" --include="*.tsx"
# Find missing metadata
find app/ -name "page.tsx" -exec grep -L "metadata\|generateMetadata" {} \;
Prioritized Fixes:
Static Metadata:
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: "Author's Info",
description: 'A social platform for book lovers',
icons: {
icon: '/images/authorsinfo-icon.svg',
apple: '/images/authorsinfo-icon.svg',
},
}
Dynamic Metadata:
import type { Metadata } from 'next'
export async function generateMetadata({ params }: { params: { id: string } }): Promise<Metadata> {
const book = await getBook(params.id)
return {
title: book.title,
description: book.description,
openGraph: {
title: book.title,
description: book.description,
images: [book.cover_image],
},
}
}
Validation:
# Check metadata in page source
curl http://localhost:3000/books/123 | grep -E "<title>|<meta"
# Verify Open Graph tags
# Test with different route params
Resources:
Common Issues:
Diagnosis:
# Find route segment configs
grep -r "export const \(dynamic\|revalidate\|runtime\|fetchCache\)" app/ --include="*.ts" --include="*.tsx"
# Check for force-dynamic usage
grep -r "dynamic.*force-dynamic" app/ --include="*.ts" --include="*.tsx"
# Find revalidate values
grep -r "revalidate.*=" app/ --include="*.ts" --include="*.tsx"
Prioritized Fixes:
export const dynamic = 'force-dynamic' for dynamic routes, set appropriate revalidate valuesConfiguration Options:
// Force dynamic rendering (no caching)
export const dynamic = 'force-dynamic'
export const revalidate = 0
// Static with ISR (revalidate every 30 minutes)
export const revalidate = 1800
// Static with ISR (revalidate every hour)
export const revalidate = 3600
// Edge runtime (faster, limited Node.js APIs)
export const runtime = 'edge'
// Disable fetch caching
export const fetchCache = 'force-no-store'
Common Patterns:
// Dynamic user pages
export const dynamic = 'force-dynamic'
// ISR for semi-static content (events, listings)
export const revalidate = 3600 // 1 hour
// Edge runtime for API routes
export const runtime = 'edge'
export const dynamic = 'force-dynamic'
Validation:
# Check build output for static/dynamic routes
npm run build | grep -E "(Static|Dynamic|ISR)"
# Test revalidation timing
# Verify Edge runtime compatibility
Resources:
Common Issues:
Diagnosis:
# Find loading states
find app/ -name "loading.tsx" -o -name "loading.js"
# Find error boundaries
find app/ -name "error.tsx" -o -name "error.js"
# Check for not-found pages
find app/ -name "not-found.tsx" -o -name "not-found.js"
# Find Suspense usage
grep -r "<Suspense" app/ --include="*.tsx" --include="*.jsx"
Prioritized Fixes:
Loading State Pattern:
// app/books/loading.tsx
export default function Loading() {
return (
<div className="space-y-4">
<div className="h-8 bg-gray-200 animate-pulse rounded" />
<div className="grid grid-cols-3 gap-4">
{[...Array(6)].map((_, i) => (
<div key={i} className="h-48 bg-gray-200 animate-pulse rounded" />
))}
</div>
</div>
)
}
Error Boundary Pattern:
// app/books/error.tsx
'use client'
import { useEffect } from 'react'
import { Button } from '@/components/ui/button'
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
useEffect(() => {
console.error('Error:', error)
}, [error])
return (
<div className="flex flex-col items-center justify-center min-h-screen">
<h2 className="text-2xl font-bold mb-4">Something went wrong!</h2>
<Button onClick={reset}>Try again</Button>
</div>
)
}
Suspense Pattern:
import { Suspense } from 'react'
import { BooksList } from './books-list'
import { BooksSkeleton } from './books-skeleton'
export default function BooksPage() {
return (
<div>
<h1>Books</h1>
<Suspense fallback={<BooksSkeleton />}>
<BooksList />
</Suspense>
</div>
)
}
Not Found Pattern:
// app/books/[id]/not-found.tsx
import Link from 'next/link'
import { Button } from '@/components/ui/button'
export default function NotFound() {
return (
<div className="flex flex-col items-center justify-center min-h-screen">
<h1 className="text-4xl font-bold mb-2">404</h1>
<h2 className="text-2xl font-semibold mb-4">Book Not Found</h2>
<Button asChild>
<Link href="/books">Back to Books</Link>
</Button>
</div>
)
}
Validation:
# Test loading states
# Navigate to routes and check for loading UI
# Test error boundaries by throwing errors
# Verify not-found pages for invalid routes
Resources:
Common Issues:
Diagnosis:
# Check next/image usage
grep -r "next/image" app/ components/ --include="*.tsx" --include="*.jsx"
# Find image configuration
grep -r "images:" next.config.* --include="*.js" --include="*.mjs" --include="*.ts"
# Check for unoptimized images
grep -r "<img" app/ components/ --include="*.tsx" --include="*.jsx"
Prioritized Fixes:
Next.config.mjs Pattern:
const nextConfig = {
images: {
qualities: [75, 95],
remotePatterns: [
{
protocol: 'https',
hostname: 'res.cloudinary.com',
pathname: '/**',
},
{
protocol: 'https',
hostname: '*.supabase.co',
pathname: '/storage/v1/object/public/**',
},
],
formats: ['image/webp', 'image/avif'],
minimumCacheTTL: 60,
},
}
Image Component Usage:
import Image from 'next/image'
// With dimensions
<Image
src={book.cover_image}
alt={book.title}
width={300}
height={450}
priority // For above-fold images
/>
// Responsive with sizes
<Image
src={book.cover_image}
alt={book.title}
width={300}
height={450}
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
/>
Security Headers Configuration:
const nextConfig = {
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'Strict-Transport-Security',
value: 'max-age=63072000; includeSubDomains; preload'
},
{
key: 'X-Frame-Options',
value: 'SAMEORIGIN'
},
{
key: 'X-Content-Type-Options',
value: 'nosniff'
},
{
key: 'Referrer-Policy',
value: 'origin-when-cross-origin'
},
]
}
]
},
}
Validation:
# Check image optimization in Network tab
# Verify WebP/AVIF format serving
# Test responsive image sizes
# Check security headers with curl
curl -I http://localhost:3000 | grep -i "x-frame\|strict-transport"
Resources:
When to Use Route Handlers:
When to Use Server Actions:
Route Handler Pattern:
// app/api/books/[id]/route.ts
import { NextRequest, NextResponse } from 'next/server'
export const dynamic = 'force-dynamic'
export async function GET(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const book = await getBook(params.id)
return NextResponse.json({ success: true, data: book })
} catch (error) {
return NextResponse.json(
{ success: false, error: 'Failed to fetch book' },
{ status: 500 }
)
}
}
export async function POST(request: NextRequest) {
const body = await request.json()
// Handle POST
}
Server Action Pattern:
// app/actions/books.ts
'use server'
import { revalidatePath } from 'next/cache'
export async function createBook(data: BookData) {
const book = await insertBook(data)
revalidatePath('/books')
return { success: true, book }
}
Decision Matrix: | Use Case | Route Handler | Server Action | |----------|---------------|---------------| | Form submission | ❌ | ✅ | | Webhook endpoint | ✅ | ❌ | | File upload | ✅ | ✅ (with formData) | | Public API | ✅ | ❌ | | Data mutation | ❌ | ✅ | | CORS needed | ✅ | ❌ | | Streaming | ✅ | ❌ |
Resources:
Route Groups: Organize routes without affecting URL structure using parentheses:
app/
(marketing)/
about/
page.tsx # /about
contact/
page.tsx # /contact
(shop)/
products/
page.tsx # /products
Parallel Routes:
Render multiple pages simultaneously using @folder convention:
app/
@analytics/
page.tsx
@team/
page.tsx
layout.tsx # Receives both @analytics and @team
Intercepting Routes:
Show modal versions of routes using (.)folder syntax:
app/
(.)photos/
[id]/
page.tsx # Intercepts /photos/[id] as modal
photos/
[id]/
page.tsx # Full page version
Resources:
When reviewing Next.js applications, focus on:
dynamictools
Webpack build optimization expert with deep knowledge of configuration patterns, bundle analysis, code splitting, module federation, performance optimization, and plugin/loader ecosystem. Use PROACTIVELY for any Webpack bundling issues including complex optimizations, build performance, custom plugins/loaders, and modern architecture patterns. If a specialized expert is a better fit, I will recommend switching and stop.
development
Web application security expert. OWASP Top 10, XSS, SQLi, CSRF, SSRF, authentication bypass, IDOR. Use for web app security testing.
testing
Vitest testing framework expert for Vite integration, Jest migration, browser mode testing, and performance optimization
tools
Vite build optimization expert with deep knowledge of ESM-first development, HMR optimization, plugin ecosystem, production builds, library mode, and SSR configuration. Use PROACTIVELY for any Vite bundling issues including dev server performance, build optimization, plugin development, and modern ESM patterns. If a specialized expert is a better fit, I will recommend switching and stop.