{
  "schemaVersion": "1.0",
  "item": {
    "slug": "afrexai-nextjs-production",
    "name": "Next.js Production Engineering",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/1kalin/afrexai-nextjs-production",
    "canonicalUrl": "https://clawhub.ai/1kalin/afrexai-nextjs-production",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/afrexai-nextjs-production",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=afrexai-nextjs-production",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "README.md",
      "SKILL.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-30T16:55:25.780Z",
      "expiresAt": "2026-05-07T16:55:25.780Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
        "contentDisposition": "attachment; filename=\"network-1.0.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/afrexai-nextjs-production"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/afrexai-nextjs-production",
    "agentPageUrl": "https://openagent3.xyz/skills/afrexai-nextjs-production/agent",
    "manifestUrl": "https://openagent3.xyz/skills/afrexai-nextjs-production/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/afrexai-nextjs-production/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "Next.js Production Engineering",
        "body": "Complete methodology for building, optimizing, and operating production Next.js applications. From architecture decisions to deployment strategies — everything beyond \"hello world.\""
      },
      {
        "title": "Quick Health Check (60 seconds)",
        "body": "Run through these 8 signals — score 0 (no) or 2 (yes):\n\nSignalCheckScore🏗️ ArchitectureServer/Client Component boundary is intentional, not accidental/2⚡ PerformanceCore Web Vitals all green (LCP <2.5s, INP <200ms, CLS <0.1)/2🔒 SecurityNo secrets in client bundles, CSP headers configured/2📦 BundleNo unnecessary client JS, tree-shaking working/2🗄️ DataCaching strategy defined (not just defaults)/2🧪 TestingE2E + unit tests in CI, >70% coverage on critical paths/2🚀 DeployPreview deploys, rollback capability, monitoring/2📊 ObservabilityError tracking, performance monitoring, structured logging/2\n\nScore: /16 → 14-16 Production-ready | 10-13 Needs work | <10 Risk zone"
      },
      {
        "title": "App Router vs Pages Router Decision",
        "body": "Default: App Router for all new projects (Next.js 13.4+).\n\nUse Pages Router ONLY if:\n\nMigrating existing Pages Router app (incremental adoption)\nTeam has zero RSC experience AND shipping deadline <2 weeks\nLibrary dependency requires Pages Router patterns"
      },
      {
        "title": "Project Structure (Recommended)",
        "body": "src/\n├── app/                    # App Router — routes only\n│   ├── (auth)/             # Route group — shared auth layout\n│   │   ├── login/page.tsx\n│   │   └── register/page.tsx\n│   ├── (dashboard)/        # Route group — shared dashboard layout\n│   │   ├── layout.tsx\n│   │   ├── page.tsx\n│   │   └── settings/page.tsx\n│   ├── api/                # Route Handlers (use sparingly)\n│   │   └── webhooks/\n│   │       └── stripe/route.ts\n│   ├── layout.tsx          # Root layout\n│   ├── loading.tsx         # Root loading\n│   ├── error.tsx           # Root error boundary\n│   ├── not-found.tsx       # 404 page\n│   └── global-error.tsx    # Global error boundary\n├── components/             # Shared components\n│   ├── ui/                 # Design system primitives\n│   ├── forms/              # Form components\n│   └── layouts/            # Layout components\n├── lib/                    # Shared utilities\n│   ├── db/                 # Database client & queries\n│   ├── auth/               # Auth utilities\n│   ├── api/                # External API clients\n│   └── utils/              # Pure utility functions\n├── hooks/                  # Custom React hooks (client-only)\n├── actions/                # Server Actions\n├── types/                  # TypeScript types\n├── styles/                 # Global styles\n└── config/                 # App configuration"
      },
      {
        "title": "Structure Rules",
        "body": "Routes are thin — page.tsx imports components, doesn't contain business logic\nComponents are reusable — never import from app/ into components/\nServer Actions get their own directory — organized by domain, not by page\nNo barrel files (index.ts re-exports) — they break tree-shaking\nColocation for route-specific — _components/ in route folders for non-shared components"
      },
      {
        "title": "Rendering Strategy Decision Matrix",
        "body": "ScenarioStrategyWhyStatic content (blog, docs, marketing)Static (SSG)Build-time generation, CDN-cachedUser-specific dashboardDynamic ServerFresh data per requestProduct listing with pricesISR (revalidate: 3600)Fresh enough, fast deliveryReal-time data (chat, stocks)Client-side + WebSocketServer can't push updatesSEO-critical + fresh dataDynamic Server + streamingFast TTFB with SuspenseHighly interactive form/wizardClient ComponentComplex state management"
      },
      {
        "title": "Server vs Client Component Rules",
        "body": "DEFAULT: Server Component (every .tsx is server by default)\n\nAdd \"use client\" ONLY when you need:\n✅ useState, useEffect, useRef, useContext\n✅ Browser APIs (window, document, localStorage)\n✅ Event handlers (onClick, onChange, onSubmit)\n✅ Third-party client libraries (framer-motion, react-hook-form)\n\nNEVER add \"use client\" because:\n❌ You want to use async/await (Server Components support this natively)\n❌ You're fetching data (fetch in Server Components, not useEffect)\n❌ You're importing a server-only library\n❌ \"It's not working\" — debug the actual issue first"
      },
      {
        "title": "The Boundary Pattern",
        "body": "// ✅ CORRECT: Server Component wraps Client Component\n// app/dashboard/page.tsx (Server Component)\nimport { getUser } from '@/lib/auth'\nimport { DashboardClient } from './_components/dashboard-client'\n\nexport default async function DashboardPage() {\n  const user = await getUser()  // Server-side data fetch\n  return <DashboardClient user={user} />  // Pass as props\n}\n\n// _components/dashboard-client.tsx\n'use client'\nexport function DashboardClient({ user }: { user: User }) {\n  const [tab, setTab] = useState('overview')\n  return <div>...</div>\n}\n\nPush \"use client\" as far down the tree as possible. The boundary should be at the leaf, not the root."
      },
      {
        "title": "Data Fetching Hierarchy (Prefer Top → Bottom)",
        "body": "Server Component direct fetch — simplest, most performant\nServer Actions — for mutations and form submissions\nRoute Handlers — for webhooks, external API endpoints\nClient-side fetch (SWR/React Query) — for real-time/polling data only"
      },
      {
        "title": "Fetch Configuration",
        "body": "// Static data (cached indefinitely, revalidated on deploy)\nconst data = await fetch('https://api.example.com/data', {\n  cache: 'force-cache'  // Default in App Router\n})\n\n// Revalidate every hour\nconst data = await fetch('https://api.example.com/data', {\n  next: { revalidate: 3600 }\n})\n\n// Always fresh (no cache)\nconst data = await fetch('https://api.example.com/data', {\n  cache: 'no-store'\n})\n\n// Tag-based revalidation\nconst data = await fetch('https://api.example.com/products', {\n  next: { tags: ['products'] }\n})\n// Then in a Server Action:\nimport { revalidateTag } from 'next/cache'\nrevalidateTag('products')"
      },
      {
        "title": "Caching Strategy by Data Type",
        "body": "Data TypeCache StrategyRevalidateTagsCMS contentISR3600s (1h)['cms', 'posts']Product catalogISR300s (5m)['products']User profileNo cache——Pricing/inventoryNo cache——Static assetsForce cacheOn deploy—Analytics/dashboardsISR60s['analytics']Auth tokensNo cache——"
      },
      {
        "title": "Database Queries (No fetch API)",
        "body": "import { unstable_cache } from 'next/cache'\nimport { db } from '@/lib/db'\n\n// Cache database queries with tags\nconst getProducts = unstable_cache(\n  async (categoryId: string) => {\n    return db.query.products.findMany({\n      where: eq(products.categoryId, categoryId)\n    })\n  },\n  ['products'],  // Cache key parts\n  {\n    revalidate: 300,\n    tags: ['products']\n  }\n)"
      },
      {
        "title": "Parallel Data Fetching",
        "body": "// ✅ CORRECT: Parallel fetches\nexport default async function DashboardPage() {\n  const [user, stats, notifications] = await Promise.all([\n    getUser(),\n    getStats(),\n    getNotifications()\n  ])\n  return <Dashboard user={user} stats={stats} notifications={notifications} />\n}\n\n// ❌ WRONG: Sequential waterfall\nexport default async function DashboardPage() {\n  const user = await getUser()\n  const stats = await getStats(user.id)  // Waits for user\n  const notifications = await getNotifications(user.id)  // Waits for stats\n}"
      },
      {
        "title": "Streaming with Suspense",
        "body": "import { Suspense } from 'react'\n\nexport default async function Page() {\n  return (\n    <div>\n      <h1>Dashboard</h1>\n      {/* Fast: renders immediately */}\n      <UserGreeting />\n      \n      {/* Slow: streams in when ready */}\n      <Suspense fallback={<StatsSkeleton />}>\n        <StatsPanel />  {/* Async Server Component */}\n      </Suspense>\n      \n      <Suspense fallback={<FeedSkeleton />}>\n        <ActivityFeed />\n      </Suspense>\n    </div>\n  )\n}"
      },
      {
        "title": "Server Action Best Practices",
        "body": "// actions/user.ts\n'use server'\n\nimport { z } from 'zod'\nimport { revalidatePath } from 'next/cache'\nimport { redirect } from 'next/navigation'\n\nconst updateProfileSchema = z.object({\n  name: z.string().min(1).max(100),\n  email: z.string().email(),\n  bio: z.string().max(500).optional()\n})\n\nexport async function updateProfile(formData: FormData) {\n  // 1. Authenticate\n  const session = await getSession()\n  if (!session) throw new Error('Unauthorized')\n\n  // 2. Validate\n  const parsed = updateProfileSchema.safeParse({\n    name: formData.get('name'),\n    email: formData.get('email'),\n    bio: formData.get('bio')\n  })\n  \n  if (!parsed.success) {\n    return { error: parsed.error.flatten().fieldErrors }\n  }\n\n  // 3. Authorize\n  if (session.userId !== formData.get('userId')) {\n    throw new Error('Forbidden')\n  }\n\n  // 4. Mutate\n  await db.update(users)\n    .set(parsed.data)\n    .where(eq(users.id, session.userId))\n\n  // 5. Revalidate\n  revalidatePath('/profile')\n  \n  return { success: true }\n}"
      },
      {
        "title": "Server Action Rules",
        "body": "Always validate input — FormData is user input, never trust it\nAlways check auth — Server Actions are public endpoints\nAlways check authorization — user can only modify their own data\nUse Zod for validation — type-safe, composable schemas\nReturn errors, don't throw — throwing shows error boundary, returning shows inline errors\nRevalidate after mutations — revalidatePath or revalidateTag\nNever return sensitive data — return only what the client needs"
      },
      {
        "title": "useActionState Pattern (React 19)",
        "body": "'use client'\nimport { useActionState } from 'react'\nimport { updateProfile } from '@/actions/user'\n\nexport function ProfileForm({ user }: { user: User }) {\n  const [state, action, pending] = useActionState(updateProfile, null)\n\n  return (\n    <form action={action}>\n      <input name=\"name\" defaultValue={user.name} />\n      {state?.error?.name && <p className=\"text-red-500\">{state.error.name}</p>}\n      \n      <button type=\"submit\" disabled={pending}>\n        {pending ? 'Saving...' : 'Save'}\n      </button>\n      \n      {state?.success && <p className=\"text-green-500\">Saved!</p>}\n    </form>\n  )\n}"
      },
      {
        "title": "Auth Pattern Selection",
        "body": "MethodBest ForLibrariesSession-based (cookie)Traditional web appsNextAuth.js / Auth.jsJWTAPI-first, mobile clientsjose, customOAuth onlySocial login, quick startNextAuth.jsPasskeys/WebAuthnModern, passwordlessSimpleWebAuthnThird-partyEnterprise, complianceClerk, Auth0, Supabase Auth"
      },
      {
        "title": "Middleware Auth Pattern",
        "body": "// middleware.ts\nimport { NextResponse } from 'next/server'\nimport type { NextRequest } from 'next/server'\n\nconst publicRoutes = ['/', '/login', '/register', '/api/webhooks']\nconst authRoutes = ['/login', '/register']\n\nexport function middleware(request: NextRequest) {\n  const { pathname } = request.nextUrl\n  const token = request.cookies.get('session')?.value\n\n  // Public routes — allow\n  if (publicRoutes.some(route => pathname.startsWith(route))) {\n    // Redirect authenticated users away from auth pages\n    if (token && authRoutes.some(route => pathname.startsWith(route))) {\n      return NextResponse.redirect(new URL('/dashboard', request.url))\n    }\n    return NextResponse.next()\n  }\n\n  // Protected routes — require auth\n  if (!token) {\n    const loginUrl = new URL('/login', request.url)\n    loginUrl.searchParams.set('callbackUrl', pathname)\n    return NextResponse.redirect(loginUrl)\n  }\n\n  return NextResponse.next()\n}\n\nexport const config = {\n  matcher: ['/((?!_next/static|_next/image|favicon.ico|public).*)']\n}"
      },
      {
        "title": "Authorization Pattern",
        "body": "// lib/auth/permissions.ts\ntype Permission = 'read' | 'write' | 'admin'\ntype Resource = 'posts' | 'users' | 'settings'\n\nconst rolePermissions: Record<string, Record<Resource, Permission[]>> = {\n  admin: {\n    posts: ['read', 'write', 'admin'],\n    users: ['read', 'write', 'admin'],\n    settings: ['read', 'write', 'admin']\n  },\n  editor: {\n    posts: ['read', 'write'],\n    users: ['read'],\n    settings: ['read']\n  },\n  viewer: {\n    posts: ['read'],\n    users: [],\n    settings: []\n  }\n}\n\nexport function can(role: string, resource: Resource, permission: Permission): boolean {\n  return rolePermissions[role]?.[resource]?.includes(permission) ?? false\n}\n\n// Usage in Server Component\nexport default async function AdminPage() {\n  const session = await getSession()\n  if (!can(session.role, 'settings', 'admin')) {\n    notFound()  // Don't reveal admin pages exist\n  }\n  return <AdminDashboard />\n}"
      },
      {
        "title": "Security Headers (next.config.ts)",
        "body": "const securityHeaders = [\n  { key: 'X-DNS-Prefetch-Control', value: 'on' },\n  { key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' },\n  { key: 'X-Frame-Options', value: 'SAMEORIGIN' },\n  { key: 'X-Content-Type-Options', value: 'nosniff' },\n  { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },\n  { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },\n  {\n    key: 'Content-Security-Policy',\n    value: `\n      default-src 'self';\n      script-src 'self' 'unsafe-eval' 'unsafe-inline';\n      style-src 'self' 'unsafe-inline';\n      img-src 'self' data: https:;\n      font-src 'self';\n      connect-src 'self' https://api.example.com;\n      frame-ancestors 'none';\n    `.replace(/\\n/g, '')\n  }\n]"
      },
      {
        "title": "Core Web Vitals Targets",
        "body": "MetricGoodNeeds ImprovementPoorLCP<2.5s2.5-4.0s>4.0sINP<200ms200-500ms>500msCLS<0.10.1-0.25>0.25TTFB<800ms800ms-1.8s>1.8sFCP<1.8s1.8-3.0s>3.0s"
      },
      {
        "title": "Image Optimization",
        "body": "import Image from 'next/image'\n\n// ✅ Always use next/image\n<Image\n  src=\"/hero.jpg\"\n  alt=\"Hero image\"\n  width={1200}\n  height={630}\n  priority  // LCP image — load immediately\n  sizes=\"(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw\"\n  placeholder=\"blur\"\n  blurDataURL={shimmer}  // Base64 placeholder\n/>\n\n// For dynamic images\n<Image\n  src={user.avatar}\n  alt={user.name}\n  width={48}\n  height={48}\n  loading=\"lazy\"  // Below fold — lazy load\n/>"
      },
      {
        "title": "Image Rules",
        "body": "Always set priority on LCP image (hero, above-fold)\nAlways provide sizes — prevents downloading oversized images\nUse placeholder=\"blur\" for large images — prevents CLS\nConfigure remotePatterns in next.config.ts for external images\nUse WebP/AVIF — next/image auto-converts by default"
      },
      {
        "title": "Bundle Optimization",
        "body": "// next.config.ts\nconst nextConfig = {\n  // Strict mode for catching bugs\n  reactStrictMode: true,\n  \n  // Optimize packages\n  experimental: {\n    optimizePackageImports: [\n      'lucide-react',\n      '@radix-ui/react-icons',\n      'date-fns',\n      'lodash-es'\n    ]\n  },\n  \n  // Bundle analyzer (dev only)\n  // npm install @next/bundle-analyzer\n  ...(process.env.ANALYZE === 'true' && {\n    webpack: (config) => {\n      const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')\n      config.plugins.push(new BundleAnalyzerPlugin({ analyzerMode: 'static' }))\n      return config\n    }\n  })\n}"
      },
      {
        "title": "Dynamic Imports for Heavy Components",
        "body": "import dynamic from 'next/dynamic'\n\n// Heavy chart library — only load when needed\nconst Chart = dynamic(() => import('@/components/chart'), {\n  loading: () => <ChartSkeleton />,\n  ssr: false  // Client-only component\n})\n\n// Code editor — definitely client-only\nconst CodeEditor = dynamic(() => import('@/components/code-editor'), {\n  ssr: false\n})"
      },
      {
        "title": "Font Optimization",
        "body": "// app/layout.tsx\nimport { Inter, JetBrains_Mono } from 'next/font/google'\n\nconst inter = Inter({\n  subsets: ['latin'],\n  display: 'swap',\n  variable: '--font-inter'\n})\n\nconst jetbrains = JetBrains_Mono({\n  subsets: ['latin'],\n  display: 'swap',\n  variable: '--font-mono'\n})\n\nexport default function RootLayout({ children }) {\n  return (\n    <html lang=\"en\" className={`${inter.variable} ${jetbrains.variable}`}>\n      <body className=\"font-sans\">{children}</body>\n    </html>\n  )\n}"
      },
      {
        "title": "Performance Budget",
        "body": "ResourceBudgetToolFirst Load JS<100KBnext build outputPage JS<50KB per routeBundle analyzerTotal page weight<500KBLighthouseLCP image<200KBnext/image handlesThird-party scripts<50KB totalScript componentWeb fonts<100KBnext/font handles"
      },
      {
        "title": "ORM Selection Guide",
        "body": "ORMBest ForTradeoffsDrizzleType-safe, lightweight, SQL-likeNewer ecosystemPrismaRapid prototyping, schema-firstHeavier, edge limitationsKyselyType-safe raw SQLMore manual, no migrationsRaw SQL (pg/mysql2)Max performance, full controlNo type safety, manual migrations"
      },
      {
        "title": "Drizzle Setup Pattern (Recommended)",
        "body": "// lib/db/index.ts\nimport { drizzle } from 'drizzle-orm/node-postgres'\nimport { Pool } from 'pg'\nimport * as schema from './schema'\n\nconst pool = new Pool({\n  connectionString: process.env.DATABASE_URL,\n  max: 20,\n  idleTimeoutMillis: 30000\n})\n\nexport const db = drizzle(pool, { schema })\n\n// lib/db/schema.ts\nimport { pgTable, text, timestamp, uuid, boolean } from 'drizzle-orm/pg-core'\n\nexport const users = pgTable('users', {\n  id: uuid('id').defaultRandom().primaryKey(),\n  email: text('email').notNull().unique(),\n  name: text('name').notNull(),\n  role: text('role', { enum: ['admin', 'editor', 'viewer'] }).default('viewer'),\n  emailVerified: boolean('email_verified').default(false),\n  createdAt: timestamp('created_at').defaultNow(),\n  updatedAt: timestamp('updated_at').defaultNow()\n})"
      },
      {
        "title": "Connection Pooling for Serverless",
        "body": "// For Vercel/serverless — use connection pooler\n// Neon: use pooler URL (port 5432 → 6543)\n// Supabase: use Supavisor URL\n// PlanetScale: serverless driver built-in\n\n// lib/db/index.ts (serverless-safe)\nimport { neon } from '@neondatabase/serverless'\nimport { drizzle } from 'drizzle-orm/neon-http'\n\nconst sql = neon(process.env.DATABASE_URL!)\nexport const db = drizzle(sql)"
      },
      {
        "title": "Test Pyramid for Next.js",
        "body": "LevelToolWhat to TestCoverage TargetUnitVitestUtils, hooks, pure functions80%+ComponentTesting Library + VitestUI components, forms70%+IntegrationTesting LibraryPage-level with mocked dataKey flowsE2EPlaywrightCritical user journeys5-10 flowsVisualPlaywright screenshotsUI regressionKey pages"
      },
      {
        "title": "Vitest Configuration",
        "body": "// vitest.config.ts\nimport { defineConfig } from 'vitest/config'\nimport react from '@vitejs/plugin-react'\nimport tsconfigPaths from 'vite-tsconfig-paths'\n\nexport default defineConfig({\n  plugins: [react(), tsconfigPaths()],\n  test: {\n    environment: 'jsdom',\n    setupFiles: ['./tests/setup.ts'],\n    include: ['**/*.test.{ts,tsx}'],\n    coverage: {\n      provider: 'v8',\n      reporter: ['text', 'lcov'],\n      exclude: ['**/*.config.*', '**/types/**']\n    }\n  }\n})"
      },
      {
        "title": "Server Component Testing",
        "body": "// Server Components can be tested as async functions\nimport { render } from '@testing-library/react'\nimport Page from '@/app/dashboard/page'\n\n// Mock the data fetching\nvi.mock('@/lib/db', () => ({\n  getUser: vi.fn().mockResolvedValue({ id: '1', name: 'Test' })\n}))\n\ntest('dashboard page renders user name', async () => {\n  const Component = await Page()  // Call as async function\n  const { getByText } = render(Component)\n  expect(getByText('Test')).toBeInTheDocument()\n})"
      },
      {
        "title": "Playwright E2E Pattern",
        "body": "// e2e/auth.spec.ts\nimport { test, expect } from '@playwright/test'\n\ntest.describe('Authentication', () => {\n  test('login flow', async ({ page }) => {\n    await page.goto('/login')\n    await page.fill('[name=\"email\"]', 'test@example.com')\n    await page.fill('[name=\"password\"]', 'password123')\n    await page.click('button[type=\"submit\"]')\n    \n    await expect(page).toHaveURL('/dashboard')\n    await expect(page.getByText('Welcome')).toBeVisible()\n  })\n  \n  test('protected route redirects', async ({ page }) => {\n    await page.goto('/dashboard')\n    await expect(page).toHaveURL(/\\/login/)\n  })\n})"
      },
      {
        "title": "Error Boundary Architecture",
        "body": "app/\n├── global-error.tsx     # Catches root layout errors (must include <html>)\n├── error.tsx            # Catches app-level errors\n├── not-found.tsx        # 404 page\n├── (dashboard)/\n│   ├── error.tsx        # Dashboard-specific errors\n│   └── settings/\n│       └── error.tsx    # Settings-specific errors"
      },
      {
        "title": "Error Component Pattern",
        "body": "// app/error.tsx\n'use client'\n\nimport { useEffect } from 'react'\n\nexport default function Error({\n  error,\n  reset,\n}: {\n  error: Error & { digest?: string }\n  reset: () => void\n}) {\n  useEffect(() => {\n    // Log to error tracking service\n    console.error('Application error:', error)\n    // Sentry.captureException(error)\n  }, [error])\n\n  return (\n    <div className=\"flex flex-col items-center justify-center min-h-[400px]\">\n      <h2 className=\"text-2xl font-bold\">Something went wrong</h2>\n      <p className=\"text-gray-500 mt-2\">\n        {error.digest ? `Error ID: ${error.digest}` : error.message}\n      </p>\n      <button\n        onClick={reset}\n        className=\"mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700\"\n      >\n        Try again\n      </button>\n    </div>\n  )\n}"
      },
      {
        "title": "Structured Logging",
        "body": "// lib/logger.ts\ntype LogLevel = 'debug' | 'info' | 'warn' | 'error'\n\nfunction log(level: LogLevel, message: string, meta?: Record<string, unknown>) {\n  const entry = {\n    timestamp: new Date().toISOString(),\n    level,\n    message,\n    ...meta,\n    // Add request context if available\n    ...(meta?.requestId && { requestId: meta.requestId })\n  }\n  \n  if (level === 'error') {\n    console.error(JSON.stringify(entry))\n  } else {\n    console.log(JSON.stringify(entry))\n  }\n}\n\nexport const logger = {\n  debug: (msg: string, meta?: Record<string, unknown>) => log('debug', msg, meta),\n  info: (msg: string, meta?: Record<string, unknown>) => log('info', msg, meta),\n  warn: (msg: string, meta?: Record<string, unknown>) => log('warn', msg, meta),\n  error: (msg: string, meta?: Record<string, unknown>) => log('error', msg, meta)\n}"
      },
      {
        "title": "Platform Comparison",
        "body": "PlatformBest ForEdgeDBCost (hobby)VercelDefault choice, best DX✅ExternalFree → $20/moCloudflare PagesEdge-first, Workers✅D1, KVFree → $5/moAWS AmplifyAWS ecosystem✅RDS, DynamoDBPay-per-useRailwayFull-stack, Docker❌Built-in Postgres$5/moFly.ioGlobal, Docker✅Built-in PostgresPay-per-useSelf-hosted (Docker)Full control❌AnyServer cost"
      },
      {
        "title": "Docker Production Setup",
        "body": "# Dockerfile\nFROM node:20-alpine AS base\nRUN corepack enable\n\nFROM base AS deps\nWORKDIR /app\nCOPY package.json pnpm-lock.yaml ./\nRUN pnpm install --frozen-lockfile\n\nFROM base AS builder\nWORKDIR /app\nCOPY --from=deps /app/node_modules ./node_modules\nCOPY . .\nENV NEXT_TELEMETRY_DISABLED=1\nRUN pnpm build\n\nFROM base AS runner\nWORKDIR /app\nENV NODE_ENV=production\nENV NEXT_TELEMETRY_DISABLED=1\n\nRUN addgroup --system --gid 1001 nodejs\nRUN adduser --system --uid 1001 nextjs\n\nCOPY --from=builder /app/public ./public\nCOPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./\nCOPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static\n\nUSER nextjs\nEXPOSE 3000\nENV PORT=3000\nENV HOSTNAME=\"0.0.0.0\"\n\nCMD [\"node\", \"server.js\"]\n\n// next.config.ts — required for standalone\nconst nextConfig = {\n  output: 'standalone'\n}"
      },
      {
        "title": "CI/CD Pipeline (GitHub Actions)",
        "body": "# .github/workflows/ci.yml\nname: CI\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\njobs:\n  quality:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: pnpm/action-setup@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: 20\n          cache: pnpm\n      - run: pnpm install --frozen-lockfile\n      - run: pnpm tsc --noEmit\n      - run: pnpm lint\n      - run: pnpm test -- --coverage\n      - run: pnpm build\n      \n  e2e:\n    runs-on: ubuntu-latest\n    needs: quality\n    steps:\n      - uses: actions/checkout@v4\n      - uses: pnpm/action-setup@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: 20\n          cache: pnpm\n      - run: pnpm install --frozen-lockfile\n      - run: pnpm exec playwright install --with-deps\n      - run: pnpm build\n      - run: pnpm exec playwright test\n      - uses: actions/upload-artifact@v4\n        if: failure()\n        with:\n          name: playwright-report\n          path: playwright-report/"
      },
      {
        "title": "Environment Variables",
        "body": "// env.ts — runtime validation with t3-env\nimport { createEnv } from '@t3-oss/env-nextjs'\nimport { z } from 'zod'\n\nexport const env = createEnv({\n  server: {\n    DATABASE_URL: z.string().url(),\n    AUTH_SECRET: z.string().min(32),\n    STRIPE_SECRET_KEY: z.string().startsWith('sk_'),\n    REDIS_URL: z.string().url().optional(),\n  },\n  client: {\n    NEXT_PUBLIC_APP_URL: z.string().url(),\n    NEXT_PUBLIC_STRIPE_KEY: z.string().startsWith('pk_'),\n  },\n  runtimeEnv: {\n    DATABASE_URL: process.env.DATABASE_URL,\n    AUTH_SECRET: process.env.AUTH_SECRET,\n    STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,\n    REDIS_URL: process.env.REDIS_URL,\n    NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,\n    NEXT_PUBLIC_STRIPE_KEY: process.env.NEXT_PUBLIC_STRIPE_KEY,\n  },\n})"
      },
      {
        "title": "Optimistic Updates",
        "body": "'use client'\nimport { useOptimistic, useTransition } from 'react'\nimport { toggleTodo } from '@/actions/todos'\n\nexport function TodoItem({ todo }: { todo: Todo }) {\n  const [optimisticTodo, setOptimisticTodo] = useOptimistic(todo)\n  const [, startTransition] = useTransition()\n\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={optimisticTodo.completed}\n        onChange={() => {\n          startTransition(async () => {\n            setOptimisticTodo({ ...todo, completed: !todo.completed })\n            await toggleTodo(todo.id)\n          })\n        }}\n      />\n      {optimisticTodo.title}\n    </label>\n  )\n}"
      },
      {
        "title": "Infinite Scroll",
        "body": "'use client'\nimport { useInView } from 'react-intersection-observer'\nimport { useEffect, useState, useTransition } from 'react'\nimport { loadMore } from '@/actions/feed'\n\nexport function InfiniteList({ initialItems }: { initialItems: Item[] }) {\n  const [items, setItems] = useState(initialItems)\n  const [cursor, setCursor] = useState(initialItems.at(-1)?.id)\n  const [hasMore, setHasMore] = useState(true)\n  const [isPending, startTransition] = useTransition()\n  const { ref, inView } = useInView()\n\n  useEffect(() => {\n    if (inView && hasMore && !isPending) {\n      startTransition(async () => {\n        const newItems = await loadMore(cursor)\n        if (newItems.length === 0) {\n          setHasMore(false)\n        } else {\n          setItems(prev => [...prev, ...newItems])\n          setCursor(newItems.at(-1)?.id)\n        }\n      })\n    }\n  }, [inView, hasMore, isPending, cursor])\n\n  return (\n    <div>\n      {items.map(item => <ItemCard key={item.id} item={item} />)}\n      {hasMore && <div ref={ref}>{isPending ? <Spinner /> : null}</div>}\n    </div>\n  )\n}"
      },
      {
        "title": "Search with URL State",
        "body": "'use client'\nimport { useRouter, useSearchParams, usePathname } from 'next/navigation'\nimport { useDebouncedCallback } from 'use-debounce'\n\nexport function SearchBar() {\n  const router = useRouter()\n  const pathname = usePathname()\n  const searchParams = useSearchParams()\n\n  const handleSearch = useDebouncedCallback((term: string) => {\n    const params = new URLSearchParams(searchParams)\n    if (term) {\n      params.set('q', term)\n      params.set('page', '1')\n    } else {\n      params.delete('q')\n    }\n    router.replace(`${pathname}?${params.toString()}`)\n  }, 300)\n\n  return (\n    <input\n      type=\"search\"\n      placeholder=\"Search...\"\n      defaultValue={searchParams.get('q') ?? ''}\n      onChange={e => handleSearch(e.target.value)}\n    />\n  )\n}"
      },
      {
        "title": "Multi-step Form with URL State",
        "body": "// app/onboarding/page.tsx\nexport default function OnboardingPage({\n  searchParams\n}: {\n  searchParams: { step?: string }\n}) {\n  const step = Number(searchParams.step) || 1\n  \n  return (\n    <div>\n      <ProgressBar step={step} total={4} />\n      {step === 1 && <StepOne />}\n      {step === 2 && <StepTwo />}\n      {step === 3 && <StepThree />}\n      {step === 4 && <StepFour />}\n    </div>\n  )\n}"
      },
      {
        "title": "Pre-Launch (Mandatory)",
        "body": "next build succeeds with zero warnings\n TypeScript strict mode, no any types in production code\n All environment variables validated (t3-env or manual)\n Security headers configured (CSP, HSTS, X-Frame-Options)\n Authentication + authorization tested (pen test critical flows)\n Error boundaries at every route level\n 404 and 500 pages customized\n Favicon, OG images, meta tags configured\n Core Web Vitals passing (Lighthouse >90)\n Mobile responsive tested on real devices\n Accessibility audit (axe, keyboard nav, screen reader)\n Rate limiting on API routes and Server Actions\n CORS configured correctly\n Database connection pooling configured for serverless\n Monitoring & error tracking connected (Sentry, etc.)"
      },
      {
        "title": "Pre-Launch (Recommended)",
        "body": "E2E tests for critical user journeys\n Bundle size within budget (<100KB first load)\n Image optimization verified (next/image, proper sizes)\n Sitemap.xml and robots.txt configured\n Analytics configured\n Preview deployment tested\n Rollback plan documented\n Load testing completed\n CDN caching verified\n Edge middleware tested in production environment"
      },
      {
        "title": "10 Next.js Mistakes",
        "body": "#MistakeFix1\"use client\" at the top of every fileDefault to Server Components, push client boundary down2Fetching data with useEffectFetch in Server Components or use SWR/React Query for client3Not using loading.tsxAdd loading states to prevent layout shift4Ignoring bundle sizeRun next build and check output, use dynamic imports5No error boundariesAdd error.tsx at every route level6Storing secrets in NEXT_PUBLIC_*Server-only env vars for secrets, validate with t3-env7Not setting image sizes propAlways provide sizes for responsive images8Sequential data fetchingUse Promise.all() for parallel fetches9Caching everything or nothingExplicit cache strategy per data type10Not using revalidateTagTag-based revalidation for precise cache control"
      },
      {
        "title": "Troubleshooting Decision Tree",
        "body": "Build error?\n├── \"Module not found\" → Check import paths, tsconfig paths\n├── \"Server Component error\" → Remove \"use client\" or move hooks to client component\n├── \"Hydration mismatch\" → Check for browser-only code in shared components\n│   → Use suppressHydrationWarning for timestamps\n│   → Wrap in useEffect or dynamic(ssr: false)\n├── \"Edge runtime error\" → Check node APIs (fs, crypto) not available at edge\n└── Slow build → Check for large static generation, reduce ISR pages\n\nRuntime error?\n├── 500 on production → Check error.tsx, logs, Sentry\n├── Slow TTFB → Check database queries, add caching\n├── CLS → Add explicit dimensions to images/embeds\n├── High JS bundle → Run bundle analyzer, dynamic import heavy libs\n└── Stale data → Check revalidation settings, revalidateTag"
      },
      {
        "title": "Recommended Stack (2025+)",
        "body": "LayerRecommendationWhyFrameworkNext.js 15+ (App Router)RSC, streaming, Server ActionsLanguageTypeScript (strict)Type safety, better DXStylingTailwind CSS 4Utility-first, no runtime costUI Componentsshadcn/uiCopy-paste, customizableFormsreact-hook-form + zodType-safe validationORMDrizzleType-safe, lightweight, SQL-likeDatabasePostgreSQL (Neon/Supabase)Serverless-friendly, provenAuthAuth.js (NextAuth v5)Built for Next.jsPaymentsStripeIndustry standardHostingVercelBest Next.js DXTestingVitest + PlaywrightFast unit + reliable E2EMonitoringSentryError tracking + performanceAnalyticsPostHogProduct analytics, open source"
      },
      {
        "title": "Quality Rubric (0-100)",
        "body": "DimensionWeightScoringArchitecture (RSC boundaries, structure)20%0-20Performance (CWV, bundle, TTFB)20%0-20Security (auth, headers, validation)15%0-15Data layer (caching, fetching, DB)15%0-15Testing (pyramid, coverage, E2E)10%0-10Error handling (boundaries, logging)10%0-10DX (types, linting, CI)5%0-5Deployment (Docker/platform, monitoring)5%0-5\n\nScore: 90-100 Elite | 75-89 Production-ready | 60-74 Needs improvement | <60 Not production-ready"
      },
      {
        "title": "Natural Language Commands",
        "body": "\"Set up a new Next.js project\" → Phase 1 architecture + structure + Phase 6 DB setup\n\"Add authentication\" → Phase 4 auth pattern + middleware + authorization\n\"Optimize performance\" → Phase 5 full checklist + image + bundle + fonts\n\"Set up testing\" → Phase 7 full pyramid + Vitest + Playwright config\n\"Deploy to production\" → Phase 9 platform selection + Docker + CI/CD + env vars\n\"Fix hydration error\" → Phase 12 troubleshooting tree\n\"Add caching\" → Phase 2 caching strategy table + fetch config + tags\n\"Create a Server Action\" → Phase 3 best practices + useActionState pattern\n\"Audit my app\" → Quick health check + Phase 11 production checklist\n\"Add error handling\" → Phase 8 error boundary architecture + logging\n\"Set up search\" → Phase 10 search with URL state pattern\n\"Review my architecture\" → Phase 1 decision matrix + rendering strategy\n\nBuilt by AfrexAI — the AI automation agency that ships. Zero dependencies."
      }
    ],
    "body": "Next.js Production Engineering\n\nComplete methodology for building, optimizing, and operating production Next.js applications. From architecture decisions to deployment strategies — everything beyond \"hello world.\"\n\nQuick Health Check (60 seconds)\n\nRun through these 8 signals — score 0 (no) or 2 (yes):\n\nSignal\tCheck\tScore\n🏗️ Architecture\tServer/Client Component boundary is intentional, not accidental\t/2\n⚡ Performance\tCore Web Vitals all green (LCP <2.5s, INP <200ms, CLS <0.1)\t/2\n🔒 Security\tNo secrets in client bundles, CSP headers configured\t/2\n📦 Bundle\tNo unnecessary client JS, tree-shaking working\t/2\n🗄️ Data\tCaching strategy defined (not just defaults)\t/2\n🧪 Testing\tE2E + unit tests in CI, >70% coverage on critical paths\t/2\n🚀 Deploy\tPreview deploys, rollback capability, monitoring\t/2\n📊 Observability\tError tracking, performance monitoring, structured logging\t/2\n\nScore: /16 → 14-16 Production-ready | 10-13 Needs work | <10 Risk zone\n\nPhase 1: Architecture Decisions\nApp Router vs Pages Router Decision\n\nDefault: App Router for all new projects (Next.js 13.4+).\n\nUse Pages Router ONLY if:\n\nMigrating existing Pages Router app (incremental adoption)\nTeam has zero RSC experience AND shipping deadline <2 weeks\nLibrary dependency requires Pages Router patterns\nProject Structure (Recommended)\nsrc/\n├── app/                    # App Router — routes only\n│   ├── (auth)/             # Route group — shared auth layout\n│   │   ├── login/page.tsx\n│   │   └── register/page.tsx\n│   ├── (dashboard)/        # Route group — shared dashboard layout\n│   │   ├── layout.tsx\n│   │   ├── page.tsx\n│   │   └── settings/page.tsx\n│   ├── api/                # Route Handlers (use sparingly)\n│   │   └── webhooks/\n│   │       └── stripe/route.ts\n│   ├── layout.tsx          # Root layout\n│   ├── loading.tsx         # Root loading\n│   ├── error.tsx           # Root error boundary\n│   ├── not-found.tsx       # 404 page\n│   └── global-error.tsx    # Global error boundary\n├── components/             # Shared components\n│   ├── ui/                 # Design system primitives\n│   ├── forms/              # Form components\n│   └── layouts/            # Layout components\n├── lib/                    # Shared utilities\n│   ├── db/                 # Database client & queries\n│   ├── auth/               # Auth utilities\n│   ├── api/                # External API clients\n│   └── utils/              # Pure utility functions\n├── hooks/                  # Custom React hooks (client-only)\n├── actions/                # Server Actions\n├── types/                  # TypeScript types\n├── styles/                 # Global styles\n└── config/                 # App configuration\n\nStructure Rules\nRoutes are thin — page.tsx imports components, doesn't contain business logic\nComponents are reusable — never import from app/ into components/\nServer Actions get their own directory — organized by domain, not by page\nNo barrel files (index.ts re-exports) — they break tree-shaking\nColocation for route-specific — _components/ in route folders for non-shared components\nRendering Strategy Decision Matrix\nScenario\tStrategy\tWhy\nStatic content (blog, docs, marketing)\tStatic (SSG)\tBuild-time generation, CDN-cached\nUser-specific dashboard\tDynamic Server\tFresh data per request\nProduct listing with prices\tISR (revalidate: 3600)\tFresh enough, fast delivery\nReal-time data (chat, stocks)\tClient-side + WebSocket\tServer can't push updates\nSEO-critical + fresh data\tDynamic Server + streaming\tFast TTFB with Suspense\nHighly interactive form/wizard\tClient Component\tComplex state management\nServer vs Client Component Rules\nDEFAULT: Server Component (every .tsx is server by default)\n\nAdd \"use client\" ONLY when you need:\n✅ useState, useEffect, useRef, useContext\n✅ Browser APIs (window, document, localStorage)\n✅ Event handlers (onClick, onChange, onSubmit)\n✅ Third-party client libraries (framer-motion, react-hook-form)\n\nNEVER add \"use client\" because:\n❌ You want to use async/await (Server Components support this natively)\n❌ You're fetching data (fetch in Server Components, not useEffect)\n❌ You're importing a server-only library\n❌ \"It's not working\" — debug the actual issue first\n\nThe Boundary Pattern\n// ✅ CORRECT: Server Component wraps Client Component\n// app/dashboard/page.tsx (Server Component)\nimport { getUser } from '@/lib/auth'\nimport { DashboardClient } from './_components/dashboard-client'\n\nexport default async function DashboardPage() {\n  const user = await getUser()  // Server-side data fetch\n  return <DashboardClient user={user} />  // Pass as props\n}\n\n// _components/dashboard-client.tsx\n'use client'\nexport function DashboardClient({ user }: { user: User }) {\n  const [tab, setTab] = useState('overview')\n  return <div>...</div>\n}\n\n\nPush \"use client\" as far down the tree as possible. The boundary should be at the leaf, not the root.\n\nPhase 2: Data Fetching & Caching\nData Fetching Hierarchy (Prefer Top → Bottom)\nServer Component direct fetch — simplest, most performant\nServer Actions — for mutations and form submissions\nRoute Handlers — for webhooks, external API endpoints\nClient-side fetch (SWR/React Query) — for real-time/polling data only\nFetch Configuration\n// Static data (cached indefinitely, revalidated on deploy)\nconst data = await fetch('https://api.example.com/data', {\n  cache: 'force-cache'  // Default in App Router\n})\n\n// Revalidate every hour\nconst data = await fetch('https://api.example.com/data', {\n  next: { revalidate: 3600 }\n})\n\n// Always fresh (no cache)\nconst data = await fetch('https://api.example.com/data', {\n  cache: 'no-store'\n})\n\n// Tag-based revalidation\nconst data = await fetch('https://api.example.com/products', {\n  next: { tags: ['products'] }\n})\n// Then in a Server Action:\nimport { revalidateTag } from 'next/cache'\nrevalidateTag('products')\n\nCaching Strategy by Data Type\nData Type\tCache Strategy\tRevalidate\tTags\nCMS content\tISR\t3600s (1h)\t['cms', 'posts']\nProduct catalog\tISR\t300s (5m)\t['products']\nUser profile\tNo cache\t—\t—\nPricing/inventory\tNo cache\t—\t—\nStatic assets\tForce cache\tOn deploy\t—\nAnalytics/dashboards\tISR\t60s\t['analytics']\nAuth tokens\tNo cache\t—\t—\nDatabase Queries (No fetch API)\nimport { unstable_cache } from 'next/cache'\nimport { db } from '@/lib/db'\n\n// Cache database queries with tags\nconst getProducts = unstable_cache(\n  async (categoryId: string) => {\n    return db.query.products.findMany({\n      where: eq(products.categoryId, categoryId)\n    })\n  },\n  ['products'],  // Cache key parts\n  {\n    revalidate: 300,\n    tags: ['products']\n  }\n)\n\nParallel Data Fetching\n// ✅ CORRECT: Parallel fetches\nexport default async function DashboardPage() {\n  const [user, stats, notifications] = await Promise.all([\n    getUser(),\n    getStats(),\n    getNotifications()\n  ])\n  return <Dashboard user={user} stats={stats} notifications={notifications} />\n}\n\n// ❌ WRONG: Sequential waterfall\nexport default async function DashboardPage() {\n  const user = await getUser()\n  const stats = await getStats(user.id)  // Waits for user\n  const notifications = await getNotifications(user.id)  // Waits for stats\n}\n\nStreaming with Suspense\nimport { Suspense } from 'react'\n\nexport default async function Page() {\n  return (\n    <div>\n      <h1>Dashboard</h1>\n      {/* Fast: renders immediately */}\n      <UserGreeting />\n      \n      {/* Slow: streams in when ready */}\n      <Suspense fallback={<StatsSkeleton />}>\n        <StatsPanel />  {/* Async Server Component */}\n      </Suspense>\n      \n      <Suspense fallback={<FeedSkeleton />}>\n        <ActivityFeed />\n      </Suspense>\n    </div>\n  )\n}\n\nPhase 3: Server Actions & Mutations\nServer Action Best Practices\n// actions/user.ts\n'use server'\n\nimport { z } from 'zod'\nimport { revalidatePath } from 'next/cache'\nimport { redirect } from 'next/navigation'\n\nconst updateProfileSchema = z.object({\n  name: z.string().min(1).max(100),\n  email: z.string().email(),\n  bio: z.string().max(500).optional()\n})\n\nexport async function updateProfile(formData: FormData) {\n  // 1. Authenticate\n  const session = await getSession()\n  if (!session) throw new Error('Unauthorized')\n\n  // 2. Validate\n  const parsed = updateProfileSchema.safeParse({\n    name: formData.get('name'),\n    email: formData.get('email'),\n    bio: formData.get('bio')\n  })\n  \n  if (!parsed.success) {\n    return { error: parsed.error.flatten().fieldErrors }\n  }\n\n  // 3. Authorize\n  if (session.userId !== formData.get('userId')) {\n    throw new Error('Forbidden')\n  }\n\n  // 4. Mutate\n  await db.update(users)\n    .set(parsed.data)\n    .where(eq(users.id, session.userId))\n\n  // 5. Revalidate\n  revalidatePath('/profile')\n  \n  return { success: true }\n}\n\nServer Action Rules\nAlways validate input — FormData is user input, never trust it\nAlways check auth — Server Actions are public endpoints\nAlways check authorization — user can only modify their own data\nUse Zod for validation — type-safe, composable schemas\nReturn errors, don't throw — throwing shows error boundary, returning shows inline errors\nRevalidate after mutations — revalidatePath or revalidateTag\nNever return sensitive data — return only what the client needs\nuseActionState Pattern (React 19)\n'use client'\nimport { useActionState } from 'react'\nimport { updateProfile } from '@/actions/user'\n\nexport function ProfileForm({ user }: { user: User }) {\n  const [state, action, pending] = useActionState(updateProfile, null)\n\n  return (\n    <form action={action}>\n      <input name=\"name\" defaultValue={user.name} />\n      {state?.error?.name && <p className=\"text-red-500\">{state.error.name}</p>}\n      \n      <button type=\"submit\" disabled={pending}>\n        {pending ? 'Saving...' : 'Save'}\n      </button>\n      \n      {state?.success && <p className=\"text-green-500\">Saved!</p>}\n    </form>\n  )\n}\n\nPhase 4: Authentication & Authorization\nAuth Pattern Selection\nMethod\tBest For\tLibraries\nSession-based (cookie)\tTraditional web apps\tNextAuth.js / Auth.js\nJWT\tAPI-first, mobile clients\tjose, custom\nOAuth only\tSocial login, quick start\tNextAuth.js\nPasskeys/WebAuthn\tModern, passwordless\tSimpleWebAuthn\nThird-party\tEnterprise, compliance\tClerk, Auth0, Supabase Auth\nMiddleware Auth Pattern\n// middleware.ts\nimport { NextResponse } from 'next/server'\nimport type { NextRequest } from 'next/server'\n\nconst publicRoutes = ['/', '/login', '/register', '/api/webhooks']\nconst authRoutes = ['/login', '/register']\n\nexport function middleware(request: NextRequest) {\n  const { pathname } = request.nextUrl\n  const token = request.cookies.get('session')?.value\n\n  // Public routes — allow\n  if (publicRoutes.some(route => pathname.startsWith(route))) {\n    // Redirect authenticated users away from auth pages\n    if (token && authRoutes.some(route => pathname.startsWith(route))) {\n      return NextResponse.redirect(new URL('/dashboard', request.url))\n    }\n    return NextResponse.next()\n  }\n\n  // Protected routes — require auth\n  if (!token) {\n    const loginUrl = new URL('/login', request.url)\n    loginUrl.searchParams.set('callbackUrl', pathname)\n    return NextResponse.redirect(loginUrl)\n  }\n\n  return NextResponse.next()\n}\n\nexport const config = {\n  matcher: ['/((?!_next/static|_next/image|favicon.ico|public).*)']\n}\n\nAuthorization Pattern\n// lib/auth/permissions.ts\ntype Permission = 'read' | 'write' | 'admin'\ntype Resource = 'posts' | 'users' | 'settings'\n\nconst rolePermissions: Record<string, Record<Resource, Permission[]>> = {\n  admin: {\n    posts: ['read', 'write', 'admin'],\n    users: ['read', 'write', 'admin'],\n    settings: ['read', 'write', 'admin']\n  },\n  editor: {\n    posts: ['read', 'write'],\n    users: ['read'],\n    settings: ['read']\n  },\n  viewer: {\n    posts: ['read'],\n    users: [],\n    settings: []\n  }\n}\n\nexport function can(role: string, resource: Resource, permission: Permission): boolean {\n  return rolePermissions[role]?.[resource]?.includes(permission) ?? false\n}\n\n// Usage in Server Component\nexport default async function AdminPage() {\n  const session = await getSession()\n  if (!can(session.role, 'settings', 'admin')) {\n    notFound()  // Don't reveal admin pages exist\n  }\n  return <AdminDashboard />\n}\n\nSecurity Headers (next.config.ts)\nconst securityHeaders = [\n  { key: 'X-DNS-Prefetch-Control', value: 'on' },\n  { key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' },\n  { key: 'X-Frame-Options', value: 'SAMEORIGIN' },\n  { key: 'X-Content-Type-Options', value: 'nosniff' },\n  { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },\n  { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },\n  {\n    key: 'Content-Security-Policy',\n    value: `\n      default-src 'self';\n      script-src 'self' 'unsafe-eval' 'unsafe-inline';\n      style-src 'self' 'unsafe-inline';\n      img-src 'self' data: https:;\n      font-src 'self';\n      connect-src 'self' https://api.example.com;\n      frame-ancestors 'none';\n    `.replace(/\\n/g, '')\n  }\n]\n\nPhase 5: Performance Optimization\nCore Web Vitals Targets\nMetric\tGood\tNeeds Improvement\tPoor\nLCP\t<2.5s\t2.5-4.0s\t>4.0s\nINP\t<200ms\t200-500ms\t>500ms\nCLS\t<0.1\t0.1-0.25\t>0.25\nTTFB\t<800ms\t800ms-1.8s\t>1.8s\nFCP\t<1.8s\t1.8-3.0s\t>3.0s\nImage Optimization\nimport Image from 'next/image'\n\n// ✅ Always use next/image\n<Image\n  src=\"/hero.jpg\"\n  alt=\"Hero image\"\n  width={1200}\n  height={630}\n  priority  // LCP image — load immediately\n  sizes=\"(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw\"\n  placeholder=\"blur\"\n  blurDataURL={shimmer}  // Base64 placeholder\n/>\n\n// For dynamic images\n<Image\n  src={user.avatar}\n  alt={user.name}\n  width={48}\n  height={48}\n  loading=\"lazy\"  // Below fold — lazy load\n/>\n\nImage Rules\nAlways set priority on LCP image (hero, above-fold)\nAlways provide sizes — prevents downloading oversized images\nUse placeholder=\"blur\" for large images — prevents CLS\nConfigure remotePatterns in next.config.ts for external images\nUse WebP/AVIF — next/image auto-converts by default\nBundle Optimization\n// next.config.ts\nconst nextConfig = {\n  // Strict mode for catching bugs\n  reactStrictMode: true,\n  \n  // Optimize packages\n  experimental: {\n    optimizePackageImports: [\n      'lucide-react',\n      '@radix-ui/react-icons',\n      'date-fns',\n      'lodash-es'\n    ]\n  },\n  \n  // Bundle analyzer (dev only)\n  // npm install @next/bundle-analyzer\n  ...(process.env.ANALYZE === 'true' && {\n    webpack: (config) => {\n      const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')\n      config.plugins.push(new BundleAnalyzerPlugin({ analyzerMode: 'static' }))\n      return config\n    }\n  })\n}\n\nDynamic Imports for Heavy Components\nimport dynamic from 'next/dynamic'\n\n// Heavy chart library — only load when needed\nconst Chart = dynamic(() => import('@/components/chart'), {\n  loading: () => <ChartSkeleton />,\n  ssr: false  // Client-only component\n})\n\n// Code editor — definitely client-only\nconst CodeEditor = dynamic(() => import('@/components/code-editor'), {\n  ssr: false\n})\n\nFont Optimization\n// app/layout.tsx\nimport { Inter, JetBrains_Mono } from 'next/font/google'\n\nconst inter = Inter({\n  subsets: ['latin'],\n  display: 'swap',\n  variable: '--font-inter'\n})\n\nconst jetbrains = JetBrains_Mono({\n  subsets: ['latin'],\n  display: 'swap',\n  variable: '--font-mono'\n})\n\nexport default function RootLayout({ children }) {\n  return (\n    <html lang=\"en\" className={`${inter.variable} ${jetbrains.variable}`}>\n      <body className=\"font-sans\">{children}</body>\n    </html>\n  )\n}\n\nPerformance Budget\nResource\tBudget\tTool\nFirst Load JS\t<100KB\tnext build output\nPage JS\t<50KB per route\tBundle analyzer\nTotal page weight\t<500KB\tLighthouse\nLCP image\t<200KB\tnext/image handles\nThird-party scripts\t<50KB total\tScript component\nWeb fonts\t<100KB\tnext/font handles\nPhase 6: Database & ORM\nORM Selection Guide\nORM\tBest For\tTradeoffs\nDrizzle\tType-safe, lightweight, SQL-like\tNewer ecosystem\nPrisma\tRapid prototyping, schema-first\tHeavier, edge limitations\nKysely\tType-safe raw SQL\tMore manual, no migrations\nRaw SQL (pg/mysql2)\tMax performance, full control\tNo type safety, manual migrations\nDrizzle Setup Pattern (Recommended)\n// lib/db/index.ts\nimport { drizzle } from 'drizzle-orm/node-postgres'\nimport { Pool } from 'pg'\nimport * as schema from './schema'\n\nconst pool = new Pool({\n  connectionString: process.env.DATABASE_URL,\n  max: 20,\n  idleTimeoutMillis: 30000\n})\n\nexport const db = drizzle(pool, { schema })\n\n// lib/db/schema.ts\nimport { pgTable, text, timestamp, uuid, boolean } from 'drizzle-orm/pg-core'\n\nexport const users = pgTable('users', {\n  id: uuid('id').defaultRandom().primaryKey(),\n  email: text('email').notNull().unique(),\n  name: text('name').notNull(),\n  role: text('role', { enum: ['admin', 'editor', 'viewer'] }).default('viewer'),\n  emailVerified: boolean('email_verified').default(false),\n  createdAt: timestamp('created_at').defaultNow(),\n  updatedAt: timestamp('updated_at').defaultNow()\n})\n\nConnection Pooling for Serverless\n// For Vercel/serverless — use connection pooler\n// Neon: use pooler URL (port 5432 → 6543)\n// Supabase: use Supavisor URL\n// PlanetScale: serverless driver built-in\n\n// lib/db/index.ts (serverless-safe)\nimport { neon } from '@neondatabase/serverless'\nimport { drizzle } from 'drizzle-orm/neon-http'\n\nconst sql = neon(process.env.DATABASE_URL!)\nexport const db = drizzle(sql)\n\nPhase 7: Testing Strategy\nTest Pyramid for Next.js\nLevel\tTool\tWhat to Test\tCoverage Target\nUnit\tVitest\tUtils, hooks, pure functions\t80%+\nComponent\tTesting Library + Vitest\tUI components, forms\t70%+\nIntegration\tTesting Library\tPage-level with mocked data\tKey flows\nE2E\tPlaywright\tCritical user journeys\t5-10 flows\nVisual\tPlaywright screenshots\tUI regression\tKey pages\nVitest Configuration\n// vitest.config.ts\nimport { defineConfig } from 'vitest/config'\nimport react from '@vitejs/plugin-react'\nimport tsconfigPaths from 'vite-tsconfig-paths'\n\nexport default defineConfig({\n  plugins: [react(), tsconfigPaths()],\n  test: {\n    environment: 'jsdom',\n    setupFiles: ['./tests/setup.ts'],\n    include: ['**/*.test.{ts,tsx}'],\n    coverage: {\n      provider: 'v8',\n      reporter: ['text', 'lcov'],\n      exclude: ['**/*.config.*', '**/types/**']\n    }\n  }\n})\n\nServer Component Testing\n// Server Components can be tested as async functions\nimport { render } from '@testing-library/react'\nimport Page from '@/app/dashboard/page'\n\n// Mock the data fetching\nvi.mock('@/lib/db', () => ({\n  getUser: vi.fn().mockResolvedValue({ id: '1', name: 'Test' })\n}))\n\ntest('dashboard page renders user name', async () => {\n  const Component = await Page()  // Call as async function\n  const { getByText } = render(Component)\n  expect(getByText('Test')).toBeInTheDocument()\n})\n\nPlaywright E2E Pattern\n// e2e/auth.spec.ts\nimport { test, expect } from '@playwright/test'\n\ntest.describe('Authentication', () => {\n  test('login flow', async ({ page }) => {\n    await page.goto('/login')\n    await page.fill('[name=\"email\"]', 'test@example.com')\n    await page.fill('[name=\"password\"]', 'password123')\n    await page.click('button[type=\"submit\"]')\n    \n    await expect(page).toHaveURL('/dashboard')\n    await expect(page.getByText('Welcome')).toBeVisible()\n  })\n  \n  test('protected route redirects', async ({ page }) => {\n    await page.goto('/dashboard')\n    await expect(page).toHaveURL(/\\/login/)\n  })\n})\n\nPhase 8: Error Handling & Monitoring\nError Boundary Architecture\napp/\n├── global-error.tsx     # Catches root layout errors (must include <html>)\n├── error.tsx            # Catches app-level errors\n├── not-found.tsx        # 404 page\n├── (dashboard)/\n│   ├── error.tsx        # Dashboard-specific errors\n│   └── settings/\n│       └── error.tsx    # Settings-specific errors\n\nError Component Pattern\n// app/error.tsx\n'use client'\n\nimport { useEffect } from 'react'\n\nexport default function Error({\n  error,\n  reset,\n}: {\n  error: Error & { digest?: string }\n  reset: () => void\n}) {\n  useEffect(() => {\n    // Log to error tracking service\n    console.error('Application error:', error)\n    // Sentry.captureException(error)\n  }, [error])\n\n  return (\n    <div className=\"flex flex-col items-center justify-center min-h-[400px]\">\n      <h2 className=\"text-2xl font-bold\">Something went wrong</h2>\n      <p className=\"text-gray-500 mt-2\">\n        {error.digest ? `Error ID: ${error.digest}` : error.message}\n      </p>\n      <button\n        onClick={reset}\n        className=\"mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700\"\n      >\n        Try again\n      </button>\n    </div>\n  )\n}\n\nStructured Logging\n// lib/logger.ts\ntype LogLevel = 'debug' | 'info' | 'warn' | 'error'\n\nfunction log(level: LogLevel, message: string, meta?: Record<string, unknown>) {\n  const entry = {\n    timestamp: new Date().toISOString(),\n    level,\n    message,\n    ...meta,\n    // Add request context if available\n    ...(meta?.requestId && { requestId: meta.requestId })\n  }\n  \n  if (level === 'error') {\n    console.error(JSON.stringify(entry))\n  } else {\n    console.log(JSON.stringify(entry))\n  }\n}\n\nexport const logger = {\n  debug: (msg: string, meta?: Record<string, unknown>) => log('debug', msg, meta),\n  info: (msg: string, meta?: Record<string, unknown>) => log('info', msg, meta),\n  warn: (msg: string, meta?: Record<string, unknown>) => log('warn', msg, meta),\n  error: (msg: string, meta?: Record<string, unknown>) => log('error', msg, meta)\n}\n\nPhase 9: Deployment & Infrastructure\nPlatform Comparison\nPlatform\tBest For\tEdge\tDB\tCost (hobby)\nVercel\tDefault choice, best DX\t✅\tExternal\tFree → $20/mo\nCloudflare Pages\tEdge-first, Workers\t✅\tD1, KV\tFree → $5/mo\nAWS Amplify\tAWS ecosystem\t✅\tRDS, DynamoDB\tPay-per-use\nRailway\tFull-stack, Docker\t❌\tBuilt-in Postgres\t$5/mo\nFly.io\tGlobal, Docker\t✅\tBuilt-in Postgres\tPay-per-use\nSelf-hosted (Docker)\tFull control\t❌\tAny\tServer cost\nDocker Production Setup\n# Dockerfile\nFROM node:20-alpine AS base\nRUN corepack enable\n\nFROM base AS deps\nWORKDIR /app\nCOPY package.json pnpm-lock.yaml ./\nRUN pnpm install --frozen-lockfile\n\nFROM base AS builder\nWORKDIR /app\nCOPY --from=deps /app/node_modules ./node_modules\nCOPY . .\nENV NEXT_TELEMETRY_DISABLED=1\nRUN pnpm build\n\nFROM base AS runner\nWORKDIR /app\nENV NODE_ENV=production\nENV NEXT_TELEMETRY_DISABLED=1\n\nRUN addgroup --system --gid 1001 nodejs\nRUN adduser --system --uid 1001 nextjs\n\nCOPY --from=builder /app/public ./public\nCOPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./\nCOPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static\n\nUSER nextjs\nEXPOSE 3000\nENV PORT=3000\nENV HOSTNAME=\"0.0.0.0\"\n\nCMD [\"node\", \"server.js\"]\n\n// next.config.ts — required for standalone\nconst nextConfig = {\n  output: 'standalone'\n}\n\nCI/CD Pipeline (GitHub Actions)\n# .github/workflows/ci.yml\nname: CI\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\njobs:\n  quality:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: pnpm/action-setup@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: 20\n          cache: pnpm\n      - run: pnpm install --frozen-lockfile\n      - run: pnpm tsc --noEmit\n      - run: pnpm lint\n      - run: pnpm test -- --coverage\n      - run: pnpm build\n      \n  e2e:\n    runs-on: ubuntu-latest\n    needs: quality\n    steps:\n      - uses: actions/checkout@v4\n      - uses: pnpm/action-setup@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: 20\n          cache: pnpm\n      - run: pnpm install --frozen-lockfile\n      - run: pnpm exec playwright install --with-deps\n      - run: pnpm build\n      - run: pnpm exec playwright test\n      - uses: actions/upload-artifact@v4\n        if: failure()\n        with:\n          name: playwright-report\n          path: playwright-report/\n\nEnvironment Variables\n// env.ts — runtime validation with t3-env\nimport { createEnv } from '@t3-oss/env-nextjs'\nimport { z } from 'zod'\n\nexport const env = createEnv({\n  server: {\n    DATABASE_URL: z.string().url(),\n    AUTH_SECRET: z.string().min(32),\n    STRIPE_SECRET_KEY: z.string().startsWith('sk_'),\n    REDIS_URL: z.string().url().optional(),\n  },\n  client: {\n    NEXT_PUBLIC_APP_URL: z.string().url(),\n    NEXT_PUBLIC_STRIPE_KEY: z.string().startsWith('pk_'),\n  },\n  runtimeEnv: {\n    DATABASE_URL: process.env.DATABASE_URL,\n    AUTH_SECRET: process.env.AUTH_SECRET,\n    STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,\n    REDIS_URL: process.env.REDIS_URL,\n    NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,\n    NEXT_PUBLIC_STRIPE_KEY: process.env.NEXT_PUBLIC_STRIPE_KEY,\n  },\n})\n\nPhase 10: Common Patterns Library\nOptimistic Updates\n'use client'\nimport { useOptimistic, useTransition } from 'react'\nimport { toggleTodo } from '@/actions/todos'\n\nexport function TodoItem({ todo }: { todo: Todo }) {\n  const [optimisticTodo, setOptimisticTodo] = useOptimistic(todo)\n  const [, startTransition] = useTransition()\n\n  return (\n    <label>\n      <input\n        type=\"checkbox\"\n        checked={optimisticTodo.completed}\n        onChange={() => {\n          startTransition(async () => {\n            setOptimisticTodo({ ...todo, completed: !todo.completed })\n            await toggleTodo(todo.id)\n          })\n        }}\n      />\n      {optimisticTodo.title}\n    </label>\n  )\n}\n\nInfinite Scroll\n'use client'\nimport { useInView } from 'react-intersection-observer'\nimport { useEffect, useState, useTransition } from 'react'\nimport { loadMore } from '@/actions/feed'\n\nexport function InfiniteList({ initialItems }: { initialItems: Item[] }) {\n  const [items, setItems] = useState(initialItems)\n  const [cursor, setCursor] = useState(initialItems.at(-1)?.id)\n  const [hasMore, setHasMore] = useState(true)\n  const [isPending, startTransition] = useTransition()\n  const { ref, inView } = useInView()\n\n  useEffect(() => {\n    if (inView && hasMore && !isPending) {\n      startTransition(async () => {\n        const newItems = await loadMore(cursor)\n        if (newItems.length === 0) {\n          setHasMore(false)\n        } else {\n          setItems(prev => [...prev, ...newItems])\n          setCursor(newItems.at(-1)?.id)\n        }\n      })\n    }\n  }, [inView, hasMore, isPending, cursor])\n\n  return (\n    <div>\n      {items.map(item => <ItemCard key={item.id} item={item} />)}\n      {hasMore && <div ref={ref}>{isPending ? <Spinner /> : null}</div>}\n    </div>\n  )\n}\n\nSearch with URL State\n'use client'\nimport { useRouter, useSearchParams, usePathname } from 'next/navigation'\nimport { useDebouncedCallback } from 'use-debounce'\n\nexport function SearchBar() {\n  const router = useRouter()\n  const pathname = usePathname()\n  const searchParams = useSearchParams()\n\n  const handleSearch = useDebouncedCallback((term: string) => {\n    const params = new URLSearchParams(searchParams)\n    if (term) {\n      params.set('q', term)\n      params.set('page', '1')\n    } else {\n      params.delete('q')\n    }\n    router.replace(`${pathname}?${params.toString()}`)\n  }, 300)\n\n  return (\n    <input\n      type=\"search\"\n      placeholder=\"Search...\"\n      defaultValue={searchParams.get('q') ?? ''}\n      onChange={e => handleSearch(e.target.value)}\n    />\n  )\n}\n\nMulti-step Form with URL State\n// app/onboarding/page.tsx\nexport default function OnboardingPage({\n  searchParams\n}: {\n  searchParams: { step?: string }\n}) {\n  const step = Number(searchParams.step) || 1\n  \n  return (\n    <div>\n      <ProgressBar step={step} total={4} />\n      {step === 1 && <StepOne />}\n      {step === 2 && <StepTwo />}\n      {step === 3 && <StepThree />}\n      {step === 4 && <StepFour />}\n    </div>\n  )\n}\n\nPhase 11: Production Checklist\nPre-Launch (Mandatory)\n next build succeeds with zero warnings\n TypeScript strict mode, no any types in production code\n All environment variables validated (t3-env or manual)\n Security headers configured (CSP, HSTS, X-Frame-Options)\n Authentication + authorization tested (pen test critical flows)\n Error boundaries at every route level\n 404 and 500 pages customized\n Favicon, OG images, meta tags configured\n Core Web Vitals passing (Lighthouse >90)\n Mobile responsive tested on real devices\n Accessibility audit (axe, keyboard nav, screen reader)\n Rate limiting on API routes and Server Actions\n CORS configured correctly\n Database connection pooling configured for serverless\n Monitoring & error tracking connected (Sentry, etc.)\nPre-Launch (Recommended)\n E2E tests for critical user journeys\n Bundle size within budget (<100KB first load)\n Image optimization verified (next/image, proper sizes)\n Sitemap.xml and robots.txt configured\n Analytics configured\n Preview deployment tested\n Rollback plan documented\n Load testing completed\n CDN caching verified\n Edge middleware tested in production environment\nPhase 12: Anti-Patterns & Troubleshooting\n10 Next.js Mistakes\n#\tMistake\tFix\n1\t\"use client\" at the top of every file\tDefault to Server Components, push client boundary down\n2\tFetching data with useEffect\tFetch in Server Components or use SWR/React Query for client\n3\tNot using loading.tsx\tAdd loading states to prevent layout shift\n4\tIgnoring bundle size\tRun next build and check output, use dynamic imports\n5\tNo error boundaries\tAdd error.tsx at every route level\n6\tStoring secrets in NEXT_PUBLIC_*\tServer-only env vars for secrets, validate with t3-env\n7\tNot setting image sizes prop\tAlways provide sizes for responsive images\n8\tSequential data fetching\tUse Promise.all() for parallel fetches\n9\tCaching everything or nothing\tExplicit cache strategy per data type\n10\tNot using revalidateTag\tTag-based revalidation for precise cache control\nTroubleshooting Decision Tree\nBuild error?\n├── \"Module not found\" → Check import paths, tsconfig paths\n├── \"Server Component error\" → Remove \"use client\" or move hooks to client component\n├── \"Hydration mismatch\" → Check for browser-only code in shared components\n│   → Use suppressHydrationWarning for timestamps\n│   → Wrap in useEffect or dynamic(ssr: false)\n├── \"Edge runtime error\" → Check node APIs (fs, crypto) not available at edge\n└── Slow build → Check for large static generation, reduce ISR pages\n\nRuntime error?\n├── 500 on production → Check error.tsx, logs, Sentry\n├── Slow TTFB → Check database queries, add caching\n├── CLS → Add explicit dimensions to images/embeds\n├── High JS bundle → Run bundle analyzer, dynamic import heavy libs\n└── Stale data → Check revalidation settings, revalidateTag\n\nRecommended Stack (2025+)\nLayer\tRecommendation\tWhy\nFramework\tNext.js 15+ (App Router)\tRSC, streaming, Server Actions\nLanguage\tTypeScript (strict)\tType safety, better DX\nStyling\tTailwind CSS 4\tUtility-first, no runtime cost\nUI Components\tshadcn/ui\tCopy-paste, customizable\nForms\treact-hook-form + zod\tType-safe validation\nORM\tDrizzle\tType-safe, lightweight, SQL-like\nDatabase\tPostgreSQL (Neon/Supabase)\tServerless-friendly, proven\nAuth\tAuth.js (NextAuth v5)\tBuilt for Next.js\nPayments\tStripe\tIndustry standard\nHosting\tVercel\tBest Next.js DX\nTesting\tVitest + Playwright\tFast unit + reliable E2E\nMonitoring\tSentry\tError tracking + performance\nAnalytics\tPostHog\tProduct analytics, open source\nQuality Rubric (0-100)\nDimension\tWeight\tScoring\nArchitecture (RSC boundaries, structure)\t20%\t0-20\nPerformance (CWV, bundle, TTFB)\t20%\t0-20\nSecurity (auth, headers, validation)\t15%\t0-15\nData layer (caching, fetching, DB)\t15%\t0-15\nTesting (pyramid, coverage, E2E)\t10%\t0-10\nError handling (boundaries, logging)\t10%\t0-10\nDX (types, linting, CI)\t5%\t0-5\nDeployment (Docker/platform, monitoring)\t5%\t0-5\n\nScore: 90-100 Elite | 75-89 Production-ready | 60-74 Needs improvement | <60 Not production-ready\n\nNatural Language Commands\n\"Set up a new Next.js project\" → Phase 1 architecture + structure + Phase 6 DB setup\n\"Add authentication\" → Phase 4 auth pattern + middleware + authorization\n\"Optimize performance\" → Phase 5 full checklist + image + bundle + fonts\n\"Set up testing\" → Phase 7 full pyramid + Vitest + Playwright config\n\"Deploy to production\" → Phase 9 platform selection + Docker + CI/CD + env vars\n\"Fix hydration error\" → Phase 12 troubleshooting tree\n\"Add caching\" → Phase 2 caching strategy table + fetch config + tags\n\"Create a Server Action\" → Phase 3 best practices + useActionState pattern\n\"Audit my app\" → Quick health check + Phase 11 production checklist\n\"Add error handling\" → Phase 8 error boundary architecture + logging\n\"Set up search\" → Phase 10 search with URL state pattern\n\"Review my architecture\" → Phase 1 decision matrix + rendering strategy\n\nBuilt by AfrexAI — the AI automation agency that ships. Zero dependencies."
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/1kalin/afrexai-nextjs-production",
    "publisherUrl": "https://clawhub.ai/1kalin/afrexai-nextjs-production",
    "owner": "1kalin",
    "version": "1.0.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/afrexai-nextjs-production",
    "downloadUrl": "https://openagent3.xyz/downloads/afrexai-nextjs-production",
    "agentUrl": "https://openagent3.xyz/skills/afrexai-nextjs-production/agent",
    "manifestUrl": "https://openagent3.xyz/skills/afrexai-nextjs-production/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/afrexai-nextjs-production/agent.md"
  }
}