gpolanco

nextjs

0
0
# Install this skill:
npx skills add gpolanco/skills-as-context --skill "nextjs"

Install specific skill from multi-skill repository

# Description

>

# SKILL.md


name: nextjs
description: >
Enforces Next.js App Router patterns: routing, caching, middleware, and metadata.
Trigger: Use when creating routes, configuring caching, setting up middleware, or handling Next.js-specific APIs.
license: Apache-2.0
metadata:
author: devcontext
version: "1.0.0"
scope: [root]
auto_invoke: "Writing Next.js code"
allowed-tools: Read


Developing with Next.js (App Router)

🚨 CRITICAL: Reference Files are MANDATORY

This SKILL.md provides OVERVIEW only. For EXACT patterns:

Task MANDATORY Reading
Creating Server Actions & data flow ⚠️ reference/data-fetching.md
Routing & file structure ⚠️ reference/routing-system.md
Architecture decisions ⚠️ reference/architecture.md

⚠️ DO NOT implement Server Actions without reading data-fetching.md FIRST.


When to Use

Use this skill for Next.js-specific patterns:

  • App Router routing conventions
  • Server Actions & data fetching flow (Actions β†’ Services β†’ Repositories)
  • Caching and revalidation
  • Middleware configuration
  • Metadata API
  • Route Handlers (webhooks, public APIs)
  • Loading and error boundaries

Cross-references:

  • For Server Actions data flow & error handling β†’ See reference/data-fetching.md
  • For React patterns (hooks, components) β†’ See react-19 skill
  • For project structure (features/, imports) β†’ See structuring-projects skill

Critical Patterns

ALWAYS

  • Use App Router - Never use Pages Router for new projects
  • Read data-fetching.md before creating Server Actions
  • Follow Action β†’ Service β†’ Repository flow (see data-fetching.md)
  • Return ApiResponse<T> from all Actions (never throw exceptions)
  • Validate ALL inputs with Zod in Actions (including IDs)
  • Implement loading.tsx for every major route segment
  • Implement error.tsx for graceful error handling
  • Use generateMetadata() for dynamic SEO
  • Use next/image with width/height to prevent CLS
  • Use next/font for optimized font loading
  • Validate env vars at build time with Zod schema
  • Use Route Handlers only for webhooks and public APIs

NEVER

  • Never use Pages Router patterns (getServerSideProps, getStaticProps)
  • Never create Route Handlers for internal mutations β†’ Use Server Actions (react-19)
  • Never use router.push() for form submissions β†’ Use Server Actions with redirect()
  • Never import next/router β†’ Use next/navigation in App Router

DEFAULTS

  • loading.tsx and error.tsx at route segment level
  • Metadata via generateMetadata() function
  • Static generation by default, opt-in to dynamic with cache: 'no-store'

🚫 Critical Anti-Patterns

  • DO NOT use router.push for navigation in Server Actions β†’ use redirect()
  • DO NOT create a Route Handler (api/route.ts) for internal data mutations β†’ use Server Actions
  • DO NOT use usePathname or useSearchParams if you can get the data from props in a Server Component
  • DO NOT ignore the Action β†’ Service β†’ Repository flow β†’ See data-fetching.md


Decision Tree

Creating a new page?        β†’ page.tsx (default export async function)
Need to navigate/redirect?  β†’ ⚠️ STOP β†’ Read reference/routing-system.md FIRST
Need loading state?         β†’ Add loading.tsx in same folder
Need error handling?        β†’ Add error.tsx ('use client') in same folder
Sharing layout?             β†’ Use layout.tsx
Need metadata?              β†’ Export generateMetadata()
Public API / webhook?       β†’ Use route.ts (Route Handler)
Internal mutation?          β†’ ⚠️ STOP β†’ Read reference/data-fetching.md FIRST
                              Then create Server Action with Action β†’ Service β†’ Repository flow
Need middleware?            β†’ middleware.ts at project root

App Router File Conventions

app/
β”œβ”€β”€ layout.tsx              # Root layout (required)
β”œβ”€β”€ page.tsx                # Home page (/)
β”œβ”€β”€ loading.tsx             # Loading UI for /
β”œβ”€β”€ error.tsx               # Error UI for / ('use client')
β”œβ”€β”€ not-found.tsx           # 404 page
β”œβ”€β”€ (auth)/                 # Route group (no URL segment)
β”‚   β”œβ”€β”€ login/page.tsx      # /login
β”‚   └── register/page.tsx   # /register
β”œβ”€β”€ dashboard/
β”‚   β”œβ”€β”€ layout.tsx          # Nested layout
β”‚   β”œβ”€β”€ page.tsx            # /dashboard
β”‚   β”œβ”€β”€ loading.tsx
β”‚   β”œβ”€β”€ error.tsx
β”‚   └── [id]/               # Dynamic segment
β”‚       └── page.tsx        # /dashboard/123
β”œβ”€β”€ api/                    # Route Handlers (public APIs only)
β”‚   └── webhooks/
β”‚       └── stripe/route.ts
└── [...slug]/              # Catch-all segment
    └── page.tsx

Metadata API

// Static metadata
export const metadata = {
  title: "Dashboard",
  description: "User dashboard",
};

// Dynamic metadata
export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const product = await getProduct(params.id);

  return {
    title: product.name,
    description: product.description,
    openGraph: {
      title: product.name,
      images: [product.image],
    },
  };
}

Caching & Revalidation

// Static (cached indefinitely) - DEFAULT
const data = await fetch("https://api.example.com/data");

// Time-based revalidation (ISR)
const data = await fetch("https://api.example.com/data", {
  next: { revalidate: 60 }, // Revalidate every 60s
});

// No cache (always fresh)
const data = await fetch("https://api.example.com/data", {
  cache: "no-store",
});

// On-demand revalidation (in Server Action)
import { revalidatePath, revalidateTag } from "next/cache";

revalidatePath("/products"); // Revalidate path
revalidateTag("products"); // Revalidate by tag

Route Handlers (Webhooks/Public APIs)

Only use for:

  • External webhooks (Stripe, GitHub)
  • Public APIs consumed by third parties
// app/api/webhooks/stripe/route.ts
import { headers } from "next/headers";

export async function POST(request: Request) {
  const body = await request.text();
  const signature = headers().get("stripe-signature")!;

  // Verify and process webhook
  const event = await verifyStripeWebhook(body, signature);

  switch (event.type) {
    case "checkout.session.completed":
      await handleCheckoutComplete(event.data.object);
      break;
  }

  return Response.json({ received: true });
}

Middleware

// 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("session");

  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*"],
};

Required Files

Every Next.js App Router project should have:

File Purpose Required
app/layout.tsx Root layout with html/body βœ… Yes
app/page.tsx Home page (/) βœ… Yes
app/loading.tsx Loading UI (Suspense boundary) Per route segment
app/error.tsx Error boundary ('use client') Per route segment
app/not-found.tsx 404 page Recommended
middleware.ts Auth/session handling If auth needed

Loading & Error Signatures

// loading.tsx - No props, returns loading UI
export default function Loading() {
  return <Skeleton />;
}

// error.tsx - Must be 'use client'
'use client';
export default function Error({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  // Return error UI with reset button
}

Environment Variables

// lib/env.ts - Validate at build time
import { z } from "zod";

const envSchema = z.object({
  DATABASE_URL: z.string().url(),
  NEXTAUTH_SECRET: z.string().min(32),
  NEXT_PUBLIC_APP_URL: z.string().url(),
});

export const env = envSchema.parse(process.env);

Naming:

  • NEXT_PUBLIC_* β†’ Available in browser
  • Others β†’ Server-only (secrets)

Commands

# Create new Next.js project
pnpm create next-app@latest ./ --typescript --tailwind --eslint --app --src-dir --import-alias "@/*"

# Development
pnpm dev

# Build & type check
pnpm build
pnpm tsc --noEmit

Resources

# Supported AI Coding Agents

This skill is compatible with the SKILL.md standard and works with all major AI coding agents:

Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.