---
name: nextjs-scaffold
description: Generate Next.js pages, API routes, server components, layouts, and middleware following project conventions. Use when user says "create page", "new route", "scaffold", "generate component", "add api endpoint", "create layout", or needs any Next.js boilerplate generated.
allowed-tools: Read, Write, Edit, Glob, Grep, Bash
---

# Next.js Scaffolding

Generate production-ready Next.js code following the project's existing patterns. Auto-detects App Router vs Pages Router, TypeScript config, and styling approach.

## Detection workflow

Before generating anything, detect the project's setup:

```bash
# 1. Router type
if [ -d "app" ] || [ -d "src/app" ]; then
  echo "APP_ROUTER"
elif [ -d "pages" ] || [ -d "src/pages" ]; then
  echo "PAGES_ROUTER"
fi

# 2. Source directory
if [ -d "src" ]; then
  echo "SRC_DIR=src"
else
  echo "SRC_DIR=."
fi

# 3. TypeScript
if [ -f "tsconfig.json" ]; then
  echo "TYPESCRIPT=true"
fi

# 4. Styling
if [ -f "tailwind.config.js" ] || [ -f "tailwind.config.ts" ]; then
  echo "TAILWIND=true"
fi

if ls src/**/*.module.css 2>/dev/null | head -1; then
  echo "CSS_MODULES=true"
fi

# 5. Database
grep -l "supabase" package.json 2>/dev/null && echo "SUPABASE=true"
grep -l "firebase" package.json 2>/dev/null && echo "FIREBASE=true"
grep -l "prisma" package.json 2>/dev/null && echo "PRISMA=true"

# 6. Auth
grep -l "next-auth\|@auth" package.json 2>/dev/null && echo "NEXTAUTH=true"
grep -l "@supabase/auth" package.json 2>/dev/null && echo "SUPABASE_AUTH=true"
```

## App Router templates

### Page (Server Component)

```typescript
// app/{route}/page.tsx
import { Metadata } from 'next'

export const metadata: Metadata = {
  title: '{Page Title}',
  description: '{Page description}',
}

export default async function {PageName}Page() {
  // Fetch data server-side
  
  return (
    <main className="container mx-auto px-4 py-8">
      <h1 className="text-3xl font-bold">{Page Title}</h1>
    </main>
  )
}
```

### Page with params

```typescript
// app/{route}/[id]/page.tsx
interface Props {
  params: Promise<{ id: string }>
}

export default async function {Name}Page({ params }: Props) {
  const { id } = await params
  
  return (
    <main className="container mx-auto px-4 py-8">
      <h1>{id}</h1>
    </main>
  )
}
```

### Layout

```typescript
// app/{route}/layout.tsx
export default function {Name}Layout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <div>
      {children}
    </div>
  )
}
```

### Loading state

```typescript
// app/{route}/loading.tsx
export default function Loading() {
  return (
    <div className="flex items-center justify-center min-h-[50vh]">
      <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900" />
    </div>
  )
}
```

### Error boundary

```typescript
// app/{route}/error.tsx
'use client'

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  return (
    <div className="flex flex-col items-center justify-center min-h-[50vh] gap-4">
      <h2 className="text-xl font-semibold">Something went wrong</h2>
      <button
        onClick={reset}
        className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
      >
        Try again
      </button>
    </div>
  )
}
```

### API Route (App Router)

```typescript
// app/api/{name}/route.ts
import { NextRequest, NextResponse } from 'next/server'

export async function GET(request: NextRequest) {
  try {
    // Implementation
    return NextResponse.json({ data: [] })
  } catch (error) {
    console.error('[API] {name} GET error:', error)
    return NextResponse.json(
      { error: 'Internal server error' },
      { status: 500 }
    )
  }
}

export async function POST(request: NextRequest) {
  try {
    const body = await request.json()
    // Validate input
    // Implementation
    return NextResponse.json({ success: true }, { status: 201 })
  } catch (error) {
    console.error('[API] {name} POST error:', error)
    return NextResponse.json(
      { error: 'Internal server error' },
      { status: 500 }
    )
  }
}
```

### Server Action

```typescript
// app/{route}/actions.ts
'use server'

import { revalidatePath } from 'next/cache'

export async function create{Name}(formData: FormData) {
  const field = formData.get('field') as string
  
  if (!field) {
    return { error: 'Field is required' }
  }

  try {
    // Database operation
    revalidatePath('/{route}')
    return { success: true }
  } catch (error) {
    return { error: 'Failed to create' }
  }
}
```

### Middleware

```typescript
// middleware.ts (project root)
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  // Auth check example
  const token = request.cookies.get('auth-token')?.value
  
  if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url))
  }
  
  return NextResponse.next()
}

export const config = {
  matcher: ['/dashboard/:path*', '/api/:path*'],
}
```

## Supabase integration patterns

### Server-side client

```typescript
// lib/supabase/server.ts
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'

export async function createClient() {
  const cookieStore = await cookies()
  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() { return cookieStore.getAll() },
        setAll(cookiesToSet) {
          cookiesToSet.forEach(({ name, value, options }) =>
            cookieStore.set(name, value, options))
        },
      },
    }
  )
}
```

### Client-side client

```typescript
// lib/supabase/client.ts
import { createBrowserClient } from '@supabase/ssr'

export function createClient() {
  return createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  )
}
```

## Scaffold workflow

1. **Detect** project setup (router, TS, styling, DB)
2. **Read** existing similar files to match patterns
3. **Generate** using the matching template
4. **Verify** the file compiles: `npx tsc --noEmit {file}`
5. **Report** what was created and where

## Scope constraints

- Always detect the project setup BEFORE generating code
- Match existing patterns (if the project uses `src/`, put files there)
- Never overwrite existing files without confirmation
- Never generate Pages Router code for App Router projects or vice versa
- Always include error handling in API routes
- Always include TypeScript types when the project uses TS
