Use when adding new error messages to React, or seeing "unknown error code" warnings.
npx skills add vercel-labs/vercel-plugin --skill "nextjs"
Install specific skill from multi-skill repository
# Description
Next.js App Router expert guidance. Use when building, debugging, or architecting Next.js applications β routing, Server Components, Server Actions, Cache Components, layouts, middleware/proxy, data fetching, rendering strategies, and deployment on Vercel.
# SKILL.md
name: nextjs
description: Next.js App Router expert guidance. Use when building, debugging, or architecting Next.js applications β routing, Server Components, Server Actions, Cache Components, layouts, middleware/proxy, data fetching, rendering strategies, and deployment on Vercel.
Next.js (v16+) β App Router
You are an expert in Next.js 16 with the App Router. Always prefer the App Router over the legacy Pages Router unless the user's project explicitly uses Pages Router.
Key Architecture
Next.js 16 uses React 19.2 features and the App Router (file-system routing under app/).
File Conventions
layout.tsxβ Persistent wrapper, preserves state across navigationspage.tsxβ Unique UI for a route, makes route publicly accessibleloading.tsxβ Suspense fallback shown while segment loadserror.tsxβ Error boundary for a segmentnot-found.tsxβ 404 UI for a segmentroute.tsβ API endpoint (Route Handler)template.tsxβ Like layout but re-mounts on navigationdefault.tsxβ Fallback for parallel routes
Routing
- Dynamic segments:
[id], catch-all:[...slug], optional catch-all:[[...slug]] - Route groups:
(group)β organize without affecting URL - Parallel routes:
@slotβ render multiple pages in same layout - Intercepting routes:
(.),(..),(...),(..)(..)β modal patterns
Server Components (Default)
All components in the App Router are Server Components by default. They:
- Run on the server only, ship zero JavaScript to the client
- Can directly await data (fetch, DB queries, file system)
- Cannot use useState, useEffect, or browser APIs
- Cannot use event handlers (onClick, onChange)
// app/users/page.tsx β Server Component (default)
export default async function UsersPage() {
const users = await db.query('SELECT * FROM users')
return <UserList users={users} />
}
Client Components
Add 'use client' at the top of the file when you need interactivity or browser APIs.
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
Rule: Push 'use client' as far down the component tree as possible. Keep data fetching in Server Components and pass data down as props.
Server Actions / Server Functions
Async functions marked with 'use server' that run on the server. Use for mutations.
// app/actions.ts
'use server'
export async function createUser(formData: FormData) {
const name = formData.get('name') as string
await db.insert('users', { name })
revalidatePath('/users')
}
Use Server Actions for:
- Form submissions and data mutations
- In-app mutations with revalidatePath / revalidateTag
Use Route Handlers (route.ts) for:
- Public APIs consumed by external clients
- Webhooks
- Large file uploads
- Streaming responses
Cache Components (Next.js 16)
The 'use cache' directive enables component and function-level caching.
'use cache'
export async function CachedUserList() {
cacheLife('hours') // Configure cache duration
cacheTag('users') // Tag for on-demand invalidation
const users = await db.query('SELECT * FROM users')
return <UserList users={users} />
}
Invalidate with updateTag('users') from a Server Action. This replaces PPR from Next.js 15 canaries.
Proxy (formerly Middleware)
In Next.js 16, middleware.ts is renamed to proxy.ts. It runs on the Node.js runtime (not Edge).
// proxy.ts
import type { NextRequest } from 'next/server'
export function proxy(request: NextRequest) {
// Rewrite, redirect, set headers, etc.
}
export const config = { matcher: ['/dashboard/:path*'] }
Breaking Changes in Next.js 16
- Async Request APIs:
cookies(),headers(),params,searchParamsare all async β mustawaitthem - Proxy replaces Middleware: Rename
middleware.tsβproxy.ts, runs on Node.js (not Edge) - Turbopack is top-level config: Move from
experimental.turbopacktoturbopackinnext.config - View Transitions: Built-in support for animating elements across navigations
Rendering Strategy Decision
| Strategy | When to Use |
|---|---|
SSG (generateStaticParams) |
Content rarely changes, maximum performance |
ISR (revalidate: N) |
Content changes periodically, acceptable staleness |
| SSR (Server Components) | Per-request fresh data, personalized content |
Cache Components ('use cache') |
Mix static shell with dynamic parts |
| Client Components | Interactive UI, browser APIs needed |
| Streaming (Suspense) | Show content progressively as data loads |
OG Image Generation
Next.js supports file-based OG image generation via opengraph-image.tsx and twitter-image.tsx special files. These use @vercel/og (built on Satori) to render JSX to images at the Edge runtime.
File Convention
Place an opengraph-image.tsx (or twitter-image.tsx) in any route segment to auto-generate social images for that route:
// app/blog/[slug]/opengraph-image.tsx
import { ImageResponse } from 'next/og'
export const runtime = 'edge'
export const alt = 'Blog post'
export const size = { width: 1200, height: 630 }
export const contentType = 'image/png'
export default async function Image({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
const post = await fetch(`https://api.example.com/posts/${slug}`).then(r => r.json())
return new ImageResponse(
(
<div
style={{
fontSize: 48,
background: 'linear-gradient(to bottom, #000, #111)',
color: 'white',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: 48,
}}
>
{post.title}
</div>
),
{ ...size }
)
}
Key Points
ImageResponseβ Import fromnext/og(re-exports@vercel/og). Renders JSX to PNG/SVG images.- Edge runtime β OG image routes run on the Edge runtime by default. Export
runtime = 'edge'explicitly for clarity. - Exports β
alt,size, andcontentTypeconfigure the generated<meta>tags automatically. - Static or dynamic β Without params, the image is generated at build time. With dynamic segments, it generates per-request.
- Supported CSS β Satori supports a Flexbox subset. Use inline
styleobjects (no Tailwind).display: 'flex'is required on containers. - Fonts β Load custom fonts via
fetchand pass toImageResponseoptions:{ fonts: [{ name, data, style, weight }] }. - Twitter fallback β If no
twitter-image.tsxexists,opengraph-image.tsxis used for Twitter cards too.
When to Use
| Approach | When |
|---|---|
opengraph-image.tsx file |
Dynamic per-route OG images with data fetching |
Static opengraph-image.png file |
Same image for every page in a segment |
generateMetadata with openGraph.images |
Point to an external image URL |
Deployment on Vercel
- Zero-config: Vercel auto-detects Next.js and optimizes
vercel devfor local development with Vercel features- Server Components β Serverless/Edge Functions automatically
- Image optimization via
next/image(automatic on Vercel) - Font optimization via
next/font(automatic on Vercel)
Common Patterns
Data Fetching in Server Components
// Parallel data fetching
const [users, posts] = await Promise.all([
getUsers(),
getPosts(),
])
Streaming with Suspense
import { Suspense } from 'react'
export default function Page() {
return (
<div>
<h1>Dashboard</h1>
<Suspense fallback={<Skeleton />}>
<SlowDataComponent />
</Suspense>
</div>
)
}
Error Handling
// app/dashboard/error.tsx
'use client'
export default function Error({ error, reset }: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<div>
<h2>Something went wrong</h2>
<button onClick={() => reset()}>Try again</button>
</div>
)
}
Official Documentation
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.