Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use when working with Next.js 15 features, App Router, Server Components, Server Actions, or data fetching patterns. Ensures correct usage of Server vs Client Components and modern Next.js patterns.
.claude/skills/aiskillstore-nextjs-15-specialist/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 110% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 61% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 138% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 268% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 161% | 0% |
Complete Next.js 15 reference for Quetrex development.
This skill provides comprehensive guidance on all Next.js 15 App Router patterns, ensuring agents implement modern Next.js correctly the first time.
These rules are NON-NEGOTIABLE. Violations will break builds.
<Image> from next/image - NEVER use <img>typescript// ✅ ALWAYS DO THIS import Image from 'next/image' <Image src="/logo.png" alt="Logo" width={200} height={100} /> <Image src={user.avatar} alt={user.name} width={40} height={40} /> // ❌ NEVER DO THIS - BUILD WILL FAIL <img src="/logo.png" alt="Logo" /> <img src={user.avatar} alt={user.name} />
Why: Next.js Image component provides automatic optimization, lazy loading, and prevents layout shift. ESLint is configured to fail builds on <img> usage.
Use this skill when working with:
This skill includes comprehensive guides covering every Next.js 15 pattern:
45+ examples covering:
35+ examples covering:
35+ examples covering:
31+ examples covering:
26+ examples covering:
Executable Python script that checks:
Run with: python validate-patterns.py /path/to/src
Do you need interactivity (onClick, onChange, etc.)?
├─ YES → Client Component ('use client')
└─ NO → Server Component (default)
Do you need React hooks (useState, useEffect)?
├─ YES → Client Component
└─ NO → Server Component
Do you need browser APIs (window, localStorage)?
├─ YES → Client Component
└─ NO → Server Component
Do you need to fetch data?
├─ Use Server Component (preferred)
└─ Only use Client Component if data must be client-side
Is the component purely presentational?
└─ Server Component (better performance)typescript// app/projects/page.tsx export default async function ProjectsPage() { const projects = await db.project.findMany() return <ProjectList projects={projects} /> }
typescript// components/ProjectCard.tsx 'use client' import { useState } from 'react' export function ProjectCard({ project }: Props) { const [loading, setLoading] = useState(false) const handleDelete = async () => { setLoading(true) await deleteProject(project.id) setLoading(false) } return ( <div> <h2>{project.name}</h2> <button onClick={handleDelete} disabled={loading}> Delete </button> </div> ) }
typescript// app/actions.ts 'use server' export async function createProject(formData: FormData) { const name = formData.get('name') as string const project = await db.project.create({ data: { name } }) revalidatePath('/projects') return { success: true, project } } // app/projects/new/page.tsx import { createProject } from '@/app/actions' export default function NewProjectPage() { return ( <form action={createProject}> <input name="name" required /> <button type="submit">Create</button> </form> ) }
typescript// app/dashboard/page.tsx import { Suspense } from 'react' export default function DashboardPage() { return ( <div> <Suspense fallback={<ProjectsSkeleton />}> <ProjectsAsync /> </Suspense> <Suspense fallback={<UsersSkeleton />}> <UsersAsync /> </Suspense> </div> ) } async function ProjectsAsync() { const projects = await fetchProjects() // Slow query return <ProjectList projects={projects} /> }
typescript// app/blog/[slug]/page.tsx import type { Metadata } from 'next' export async function generateMetadata({ params, }: { params: Promise<{ slug: string }> }): Promise<Metadata> { const { slug } = await params const post = await fetchPost(slug) return { title: post.title, description: post.excerpt, openGraph: { title: post.title, description: post.excerpt, images: [post.coverImage], }, } }
typescript// ✅ DO: Server Component (default) export default async function ProjectsPage() { const projects = await fetchProjects() return <ProjectList projects={projects} /> } // ❌ DON'T: Client Component when not needed 'use client' export default function ProjectsPage() { const [projects, setProjects] = useState([]) useEffect(() => { fetchProjects().then(setProjects) }, []) return <ProjectList projects={projects} /> }
typescript// Static content (cached forever) const categories = await fetch('https://api.example.com/categories', { cache: 'force-cache', }).then(r => r.json()) // Dynamic content (no cache) const user = await fetch('https://api.example.com/me', { cache: 'no-store', }).then(r => r.json()) // ISR (revalidate every hour) const products = await fetch('https://api.example.com/products', { next: { revalidate: 3600 }, }).then(r => r.json())
typescript// app/dashboard/error.tsx 'use client' export default function DashboardError({ error, reset, }: { error: Error reset: () => void }) { return ( <div> <h2>Something went wrong!</h2> <button onClick={reset}>Try again</button> </div> ) }
typescript// app/dashboard/loading.tsx export default function DashboardLoading() { return <DashboardSkeleton /> }
typescript// ✅ DO: Use next/image import Image from 'next/image' export function ProjectCard({ project }) { return ( <Image src={project.image} alt={project.name} width={400} height={300} /> ) } // ❌ DON'T: Use <img> tag export function ProjectCard({ project }) { return <img src={project.image} alt={project.name} /> }
typescript// ❌ DON'T: This is a syntax error 'use client' export default async function BadComponent() { const data = await fetch('/api/data') return <div>{data}</div> } // ✅ DO: Use Server Component or useEffect export default async function GoodComponent() { const data = await fetch('/api/data') return <div>{data}</div> }
typescript// ❌ DON'T: Server Components can't use browser APIs export default function BadComponent() { const [state, setState] = useState(false) // Error! return <div>{state}</div> } // ✅ DO: Add 'use client' directive 'use client' export default function GoodComponent() { const [state, setState] = useState(false) return <div>{state}</div> }
typescript// ❌ DON'T: Unclear caching behavior const data = await fetch('/api/data') // ✅ DO: Explicit cache strategy const data = await fetch('/api/data', { cache: 'no-store', // or 'force-cache', or { next: { revalidate: 60 } } })
typescript// ❌ DON'T: Unoptimized images <img src="/logo.png" alt="Logo" /> // ✅ DO: Use Next.js Image optimization <Image src="/logo.png" alt="Logo" width={200} height={100} />
Solution: Add 'use client' to the component file.
Solution: Remove 'use client' or use useEffect instead of async component.
Solution: Environment variables in Client Components need NEXT_PUBLIC_ prefix.
Solution: Don't use headers() or cookies() after sending response. Call them before any streaming.
Run the pattern validator to check your code:
bashpython .claude/skills/nextjs-15-specialist/validate-patterns.py src/
The validator checks for:
This skill ensures you:
When in doubt:
Last updated: 2025-11-23 Next.js Version: 15.5 Total Examples: 150+ Total Lines: 4,000+
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 14,492 | 18,954 | +31% | 1 | 1 | 0% | 3,151 | 6,617 | +110% | 0 | 0 | — |
case-02 | pass→pass | 18,951 | 21,478 | +13% | 1 | 1 | 0% | 4,471 | 7,217 | +61% | 0 | 0 | — |
case-03 | pass→pass | 12,733 | 13,974 | +10% | 1 | 1 | 0% | 2,189 | 5,200 | +138% | 0 | 0 | — |
case-04 | pass→pass | 11,559 | 11,974 | +4% | 1 | 1 | 0% | 1,320 | 4,853 | +268% | 0 | 0 | — |
case-05 | pass→pass | 17,217 | 18,511 | +8% | 1 | 1 | 0% | 2,456 | 6,399 | +161% | 0 | 0 | — |
case-06 | pass→pass | 16,734 | 8,575 | -49% | 1 | 1 | 0% | 1,222 | 5,016 | +310% | 0 | 0 | — |
case-07 | pass→pass | 14,829 | 19,198 | +29% | 1 | 1 | 0% | 3,105 | 6,031 | +94% | 0 | 0 | — |
case-08 | pass→pass | 16,845 | 17,577 | +4% | 1 | 1 | 0% | 3,643 | 6,196 | +70% | 0 | 0 | — |
case-09 | pass→pass | 18,257 | 20,416 | +12% | 1 | 1 | 0% | 4,080 | 6,839 | +68% | 0 | 0 | — |
case-10 | pass→pass | 14,827 | 7,517 | -49% | 1 | 1 | 0% | 1,739 | 4,844 | +179% | 0 | 0 | — |
case-11 | pass→pass | 18,879 | 17,601 | -7% | 1 | 1 | 0% | 3,136 | 6,265 | +100% | 0 | 0 | — |
case-12 | pass→pass | 18,077 | 20,259 | +12% | 1 | 1 | 0% | 3,936 | 6,710 | +70% | 0 | 0 | — |
case-13 | pass→pass | 21,763 | 23,637 | +9% | 1 | 1 | 0% | 3,505 | 7,460 | +113% | 0 | 0 | — |
case-14 | pass→pass | 9,226 | 14,434 | +56% | 1 | 1 | 0% | 1,865 | 5,811 | +212% | 0 | 0 | — |
case-15 | pass→pass | 25,652 | 21,314 | -17% | 1 | 1 | 0% | 4,040 | 8,655 | +114% | 0 | 0 | — |
case-16 | pass→pass | 32,053 | 6,550 | -80% | 1 | 1 | 0% | 1,408 | 4,620 | +228% | 0 | 0 | — |
case-17 | pass→pass | 18,773 | 17,642 | -6% | 1 | 1 | 0% | 1,847 | 6,095 | +230% | 0 | 0 | — |
case-18 | pass→pass | 27,927 | 15,484 | -45% | 1 | 1 | 0% | 2,214 | 5,711 | +158% | 0 | 0 | — |
case-19 | pass→pass | 12,321 | 13,412 | +9% | 1 | 1 | 0% | 1,576 | 5,265 | +234% | 0 | 0 | — |
case-20 | pass→pass | 13,671 | 31,248 | +129% | 1 | 1 | 0% | 1,796 | 5,191 | +189% | 0 | 0 | — |
case-21 | pass→pass | 18,169 | 19,128 | +5% | 1 | 1 | 0% | 2,666 | 6,209 | +133% | 0 | 0 | — |
case-22 | pass→pass | 23,506 | 18,428 | -22% | 1 | 1 | 0% | 2,508 | 6,049 | +141% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +5 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.