From nextjs-pro
Next.js App Router, Server Components, Server Actions, ISR, streaming, middleware. Use when building or reviewing Next.js apps.
How this skill is triggered — by the user, by Claude, or both
Slash command
/nextjs-pro:nextjs-proThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Implement Next.js applications using production-proven patterns. Covers App Router, React Server Components, data fetching, Server Actions, streaming, ISR, Middleware, and performance optimization.
Implement Next.js applications using production-proven patterns. Covers App Router, React Server Components, data fetching, Server Actions, streaming, ISR, Middleware, and performance optimization.
Use this when:
Use this ESPECIALLY when:
'use client' is spreading across the treeDon't skip when:
useEffect data fetch still in place — Server Components may make it unnecessary and harmfulapp/
(marketing)/
page.tsx ← Public landing
layout.tsx ← Marketing layout (no auth wrapper)
(dashboard)/
layout.tsx ← Auth-required layout
page.tsx ← Dashboard home
projects/
page.tsx ← /projects (paginated list)
[id]/
page.tsx ← /projects/:id
settings/
page.tsx ← /projects/:id/settings
api/
v1/
projects/
route.ts ← GET, POST /api/v1/projects
[id]/
route.ts ← GET, PATCH, DELETE /api/v1/projects/:id
| Pattern | Use Case | Example |
|---|---|---|
| RSC (default) | Initial page data | async function Page() |
| React Query | Client-side mutations + refetch | TanStack Query, SWR |
| Server Actions | Form submissions | 'use server' functions |
| Route Handlers | External API consumption | Webhooks, mobile clients |
// ✅ RSC: Fetch on server, zero client JS
async function ProjectPage({ params }: { params: { id: string } }) {
const project = await db.projects.findUnique({
where: { id: params.id },
include: { tasks: true },
})
if (!project) notFound()
return <ProjectDetail project={project} />
}
// ✅ Server Action for mutations
async function createProject(formData: FormData) {
'use server'
const name = formData.get('name') as string
await db.projects.create({ data: { name } })
revalidatePath('/projects')
}
// ✅ Parallel routes for complex layouts
app/
@analytics/ ← Parallel route (analytics panel)
@chat/ ← Parallel route (chat panel)
page.tsx ← Default view
// ✅ Intercepting routes for modals
app/
feed/
page.tsx ← /feed
photo/
[id]/
page.tsx ← /feed/photo/:id (full page)
@modal/
photo/
[id]/
page.tsx ← Intercepted modal
// error.tsx — per-route error boundary
'use client'
export default function Error({ error, reset }: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<ErrorState
message={error.message}
onRetry={reset}
/>
)
}
// not-found.tsx — per-route 404
export default function NotFound() {
return <EmptyState message="Project not found" />
}
// global-error.tsx — last resort (replaces root layout)
export default function GlobalError({ error, reset }: Props) {
return <html><body><ErrorFallback /></body></html>
}
// loading.tsx — per-route loading state
export default function Loading() {
return <ProjectListSkeleton />
}
// Streaming with Suspense
async function ProjectPage() {
return (
<div>
<h1>Project Dashboard</h1>
<Suspense fallback={<StatsSkeleton />}>
<ProjectStats /> ← Slow data (streamed)
</Suspense>
<Suspense fallback={<ListSkeleton />}>
<ProjectList /> ← Fast data (renders first)
</Suspense>
</div>
)
}
// middleware.ts
export function middleware(request: NextRequest) {
const token = request.cookies.get('session')?.value
// Protected routes
if (request.nextUrl.pathname.startsWith('/dashboard')) {
if (!token) {
return NextResponse.redirect(new URL('/login', request.url))
}
}
// Internationalization
if (request.nextUrl.pathname.startsWith('/en') || ...) {
// rewrite to locale-specific page
}
}
export const config = {
matcher: ['/dashboard/:path*', '/api/v1/:path*'],
}
// Incremental Static Regeneration
async function BlogPage() {
const posts = await fetch('https://api.example.com/posts', {
next: { revalidate: 3600 } // Revalidate every hour
})
return <BlogList posts={posts} />
}
// On-demand revalidation
// POST /api/revalidate
export async function POST(request: Request) {
const { path } = await request.json()
revalidatePath(path)
return Response.json({ revalidated: true })
}
'use client' only when needed)next/image with sizes propnext/fontdynamic = 'force-static' where possible)useEffect for data fetching (use RSC or React Query)generateMetadata)Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub haj1t/senior-dev-squad-skills --plugin nextjs-pro