diff --git a/README.md b/README.md index 1aa92d6..e4d535a 100644 --- a/README.md +++ b/README.md @@ -55,15 +55,11 @@ A text-form relational graph covering: | `env-vars` | Environment variable management — .env files, vercel env commands, OIDC tokens | | `knowledge-update` | Knowledge update guidance for the plugin | | `marketplace` | Integration discovery, installation, auto-provisioned env vars, unified billing | -| `next-cache-components` | Next.js 16 Cache Components — PPR, `use cache`, cacheLife, cacheTag, updateTag | | `next-forge` | Production SaaS monorepo starter — Turborepo, Clerk, Prisma/Neon, Stripe, shadcn/ui | -| `next-upgrade` | Next.js version upgrades — codemods, migration guides, dependency updates | -| `nextjs` | App Router, Server Components, Server Actions, Cache Components, routing, rendering strategies | | `react-best-practices` | React/Next.js performance optimization — 64 rules across 8 categories | | `routing-middleware` | Request interception before cache, rewrites, redirects, personalization — Edge/Node.js/Bun runtimes | | `runtime-cache` | Ephemeral per-region key-value cache, tag-based invalidation, shared across Functions/Middleware/Builds | | `shadcn` | shadcn/ui — CLI, component installation, custom registries, theming, Tailwind CSS integration | -| `turbopack` | Next.js bundler, HMR, configuration, Turbopack vs Webpack | | `vercel-agent` | AI-powered code review, incident investigation, SDK installation, PR analysis | | `vercel-cli` | All CLI commands — deploy, env, dev, domains, cache management, MCP integration, marketplace | | `vercel-functions` | Serverless, Edge, Fluid Compute, streaming, Cron Jobs, configuration | @@ -185,10 +181,7 @@ skills// |-------|--------------| | `ai-sdk` | [vercel/ai](https://github.com/vercel/ai) | | `chat-sdk` | [vercel/chat](https://github.com/vercel/chat) | -| `next-cache-components` | [vercel-labs/next-skills](https://github.com/vercel-labs/next-skills) | | `next-forge` | [vercel/next-forge](https://github.com/vercel/next-forge) | -| `next-upgrade` | [vercel-labs/next-skills](https://github.com/vercel-labs/next-skills) | -| `nextjs` | [vercel-labs/next-skills](https://github.com/vercel-labs/next-skills) | | `react-best-practices` | [vercel-labs/agent-skills](https://github.com/vercel-labs/agent-skills) | | `vercel-cli` | [vercel/vercel](https://github.com/vercel/vercel) | | `vercel-sandbox` | [vercel-labs/agent-browser](https://github.com/vercel-labs/agent-browser) | diff --git a/agents/performance-optimizer.md b/agents/performance-optimizer.md index b93725d..e798c1a 100644 --- a/agents/performance-optimizer.md +++ b/agents/performance-optimizer.md @@ -117,272 +117,6 @@ CLS > 0.1? --- -## Rendering Strategy Decision Tree - - -# RSC Boundaries - -Detect and prevent invalid patterns when crossing Server/Client component boundaries. - -## Detection Rules - -### 1. Async Client Components Are Invalid - -Client components **cannot** be async functions. Only Server Components can be async. - -**Detect:** File has `'use client'` AND component is `async function` or returns `Promise` - -```tsx -// Bad: async client component -'use client' -export default async function UserProfile() { - const user = await getUser() // Cannot await in client component - return
{user.name}
-} - -// Good: Remove async, fetch data in parent server component -// page.tsx (server component - no 'use client') -export default async function Page() { - const user = await getUser() - return -} - -// UserProfile.tsx (client component) -'use client' -export function UserProfile({ user }: { user: User }) { - return
{user.name}
-} -``` - -```tsx -// Bad: async arrow function client component -'use client' -const Dashboard = async () => { - const data = await fetchDashboard() - return
{data}
-} - -// Good: Fetch in server component, pass data down -``` - -### 2. Non-Serializable Props to Client Components - -Props passed from Server → Client must be JSON-serializable. - -**Detect:** Server component passes these to a client component: -- Functions (except Server Actions with `'use server'`) -- `Date` objects -- `Map`, `Set`, `WeakMap`, `WeakSet` -- Class instances -- `Symbol` (unless globally registered) -- Circular references - -```tsx -// Bad: Function prop -// page.tsx (server) -export default function Page() { - const handleClick = () => console.log('clicked') - return -} - -// Good: Define function inside client component -// ClientButton.tsx -'use client' -export function ClientButton() { - const handleClick = () => console.log('clicked') - return -} -``` - -```tsx -// Bad: Date object (silently becomes string, then crashes) -// page.tsx (server) -export default async function Page() { - const post = await getPost() - return // Date object -} - -// PostCard.tsx (client) - will crash on .getFullYear() -'use client' -export function PostCard({ createdAt }: { createdAt: Date }) { - return {createdAt.getFullYear()} // Runtime error! -} - -// Good: Serialize to string on server -// page.tsx (server) -export default async function Page() { - const post = await getPost() - return -} - -// PostCard.tsx (client) -'use client' -export function PostCard({ createdAt }: { createdAt: string }) { - const date = new Date(createdAt) - return {date.getFullYear()} -} -``` - -```tsx -// Bad: Class instance -const user = new UserModel(data) - // Methods will be stripped - -// Good: Pass plain object -const user = await getUser() - -``` - -```tsx -// Bad: Map/Set - - -// Good: Convert to array/object - - -``` - -### 3. Server Actions Are the Exception - -Functions marked with `'use server'` CAN be passed to client components. - -```tsx -// Valid: Server Action can be passed -// actions.ts -'use server' -export async function submitForm(formData: FormData) { - // server-side logic -} - -// page.tsx (server) -import { submitForm } from './actions' -export default function Page() { - return // OK! -} - -// ClientForm.tsx (client) -'use client' -export function ClientForm({ onSubmit }: { onSubmit: (data: FormData) => Promise }) { - return
...
-} -``` - -## Quick Reference - -| Pattern | Valid? | Fix | -|---------|--------|-----| -| `'use client'` + `async function` | No | Fetch in server parent, pass data | -| Pass `() => {}` to client | No | Define in client or use server action | -| Pass `new Date()` to client | No | Use `.toISOString()` | -| Pass `new Map()` to client | No | Convert to object/array | -| Pass class instance to client | No | Pass plain object | -| Pass server action to client | Yes | - | -| Pass `string/number/boolean` | Yes | - | -| Pass plain object/array | Yes | - | - ---- - -## Bundle Size Analysis - - -Analyze bundle size with the built-in analyzer (Next.js 16.1+): - -```bash -next experimental-analyze -``` - -This opens an interactive UI to: -- Filter by route, environment (client/server), and type -- Inspect module sizes and import chains -- View treemap visualization - -Save output for comparison: - -```bash -next experimental-analyze --output -# Output saved to .next/diagnostics/analyze -``` - -Reference: https://nextjs.org/docs/app/guides/package-bundling - ---- - -## Caching Strategy Matrix - - -``` -Need to fetch data? -├── From a Server Component? -│ └── Use: Fetch directly (no API needed) -│ -├── From a Client Component? -│ ├── Is it a mutation (POST/PUT/DELETE)? -│ │ └── Use: Server Action -│ └── Is it a read (GET)? -│ └── Use: Route Handler OR pass from Server Component -│ -├── Need external API access (webhooks, third parties)? -│ └── Use: Route Handler -│ -└── Need REST API for mobile app / external clients? - └── Use: Route Handler -``` - -### Cache Invalidation Patterns - - -### `cacheTag()` - Tag Cached Content - -```tsx -import { cacheTag } from 'next/cache' - -async function getProducts() { - 'use cache' - cacheTag('products') - return db.products.findMany() -} - -async function getProduct(id: string) { - 'use cache' - cacheTag('products', `product-${id}`) - return db.products.findUnique({ where: { id } }) -} -``` - -### `updateTag()` - Immediate Invalidation - -Use when you need the cache refreshed within the same request: - -```tsx -'use server' - -import { updateTag } from 'next/cache' - -export async function updateProduct(id: string, data: FormData) { - await db.products.update({ where: { id }, data }) - updateTag(`product-${id}`) // Immediate - same request sees fresh data -} -``` - -### `revalidateTag()` - Background Revalidation - -Use for stale-while-revalidate behavior: - -```tsx -'use server' - -import { revalidateTag } from 'next/cache' - -export async function createPost(data: FormData) { - await db.posts.create({ data }) - revalidateTag('posts') // Background - next request sees fresh data -} -``` - ---- - ---- - ## Performance Audit Checklist Run through this when asked to optimize a Vercel application: @@ -400,536 +134,6 @@ Run through this when asked to optimize a Vercel application: --- -## Specific Fix Patterns - -### Image Optimization - - -# Image Optimization - -Use `next/image` for automatic image optimization. - -## Always Use next/image - -```tsx -// Bad: Avoid native img -Hero - -// Good: Use next/image -import Image from 'next/image' -Hero -``` - -## Required Props - -Images need explicit dimensions to prevent layout shift: - -```tsx -// Local images - dimensions inferred automatically -import heroImage from './hero.png' -Hero - -// Remote images - must specify width/height -Hero - -// Or use fill for parent-relative sizing -
- Hero -
-``` - -## Remote Images Configuration - -Remote domains must be configured in `next.config.js`: - -```js -// next.config.js -module.exports = { - images: { - remotePatterns: [ - { - protocol: 'https', - hostname: 'example.com', - pathname: '/images/**', - }, - { - protocol: 'https', - hostname: '*.cdn.com', // Wildcard subdomain - }, - ], - }, -} -``` - -## Responsive Images - -Use `sizes` to tell the browser which size to download: - -```tsx -// Full-width hero -Hero - -// Responsive grid (3 columns on desktop, 1 on mobile) -Card - -// Fixed sidebar image -Avatar -``` - -## Blur Placeholder - -Prevent layout shift with placeholders: - -```tsx -// Local images - automatic blur hash -import heroImage from './hero.png' -Hero - -// Remote images - provide blurDataURL -Hero - -// Or use color placeholder -Hero -``` - -## Priority Loading - -Use `priority` for above-the-fold images (LCP): - -```tsx -// Hero image - loads immediately -Hero - -// Below-fold images - lazy loaded by default (no priority needed) -Card -``` - -## Common Mistakes - -```tsx -// Bad: Missing sizes with fill - downloads largest image -Hero - -// Good: Add sizes for proper responsive behavior -Hero - -// Bad: Using width/height for aspect ratio only -Hero - -// Good: Use actual display dimensions or fill with sizes -Hero - -// Bad: Remote image without config -Image -// Error: Invalid src prop, hostname not configured - -// Good: Add hostname to next.config.js remotePatterns -``` - -## Static Export - -When using `output: 'export'`, use `unoptimized` or custom loader: - -```tsx -// Option 1: Disable optimization -Hero - -// Option 2: Global config -// next.config.js -module.exports = { - output: 'export', - images: { unoptimized: true }, -} - -// Option 3: Custom loader (Cloudinary, Imgix, etc.) -const cloudinaryLoader = ({ src, width, quality }) => { - return `https://res.cloudinary.com/demo/image/upload/w_${width},q_${quality || 75}/${src}` -} - -Sample -``` - -### Font Loading - - -# Font Optimization - -Use `next/font` for automatic font optimization with zero layout shift. - -## Google Fonts - -```tsx -// app/layout.tsx -import { Inter } from 'next/font/google' - -const inter = Inter({ subsets: ['latin'] }) - -export default function RootLayout({ children }: { children: React.ReactNode }) { - return ( - - {children} - - ) -} -``` - -## Multiple Fonts - -```tsx -import { Inter, Roboto_Mono } from 'next/font/google' - -const inter = Inter({ - subsets: ['latin'], - variable: '--font-inter', -}) - -const robotoMono = Roboto_Mono({ - subsets: ['latin'], - variable: '--font-roboto-mono', -}) - -export default function RootLayout({ children }: { children: React.ReactNode }) { - return ( - - {children} - - ) -} -``` - -Use in CSS: -```css -body { - font-family: var(--font-inter); -} - -code { - font-family: var(--font-roboto-mono); -} -``` - -## Font Weights and Styles - -```tsx -// Single weight -const inter = Inter({ - subsets: ['latin'], - weight: '400', -}) - -// Multiple weights -const inter = Inter({ - subsets: ['latin'], - weight: ['400', '500', '700'], -}) - -// Variable font (recommended) - includes all weights -const inter = Inter({ - subsets: ['latin'], - // No weight needed - variable fonts support all weights -}) - -// With italic -const inter = Inter({ - subsets: ['latin'], - style: ['normal', 'italic'], -}) -``` - -## Local Fonts - -```tsx -import localFont from 'next/font/local' - -const myFont = localFont({ - src: './fonts/MyFont.woff2', -}) - -// Multiple files for different weights -const myFont = localFont({ - src: [ - { - path: './fonts/MyFont-Regular.woff2', - weight: '400', - style: 'normal', - }, - { - path: './fonts/MyFont-Bold.woff2', - weight: '700', - style: 'normal', - }, - ], -}) - -// Variable font -const myFont = localFont({ - src: './fonts/MyFont-Variable.woff2', - variable: '--font-my-font', -}) -``` - -## Tailwind CSS Integration - -```tsx -// app/layout.tsx -import { Inter } from 'next/font/google' - -const inter = Inter({ - subsets: ['latin'], - variable: '--font-inter', -}) - -export default function RootLayout({ children }) { - return ( - - {children} - - ) -} -``` - -```js -// tailwind.config.js -module.exports = { - theme: { - extend: { - fontFamily: { - sans: ['var(--font-inter)'], - }, - }, - }, -} -``` - -## Preloading Subsets - -Only load needed character subsets: - -```tsx -// Latin only (most common) -const inter = Inter({ subsets: ['latin'] }) - -// Multiple subsets -const inter = Inter({ subsets: ['latin', 'latin-ext', 'cyrillic'] }) -``` - -## Display Strategy - -Control font loading behavior: - -```tsx -const inter = Inter({ - subsets: ['latin'], - display: 'swap', // Default - shows fallback, swaps when loaded -}) - -// Options: -// 'auto' - browser decides -// 'block' - short block period, then swap -// 'swap' - immediate fallback, swap when ready (recommended) -// 'fallback' - short block, short swap, then fallback -// 'optional' - short block, no swap (use if font is optional) -``` - -## Don't Use Manual Font Links - -Always use `next/font` instead of `` tags for Google Fonts. - -```tsx -// Bad: Manual link tag (blocks rendering, no optimization) - - -// Bad: Missing display and preconnect - - -// Good: Use next/font (self-hosted, zero layout shift) -import { Inter } from 'next/font/google' - -const inter = Inter({ subsets: ['latin'] }) -``` - -## Common Mistakes - -```tsx -// Bad: Importing font in every component -// components/Button.tsx -import { Inter } from 'next/font/google' -const inter = Inter({ subsets: ['latin'] }) // Creates new instance each time! - -// Good: Import once in layout, use CSS variable -// app/layout.tsx -const inter = Inter({ subsets: ['latin'], variable: '--font-inter' }) - -// Bad: Using @import in CSS (blocks rendering) -/* globals.css */ -@import url('https://fonts.googleapis.com/css2?family=Inter'); - -// Good: Use next/font (self-hosted, no network request) -import { Inter } from 'next/font/google' - -// Bad: Loading all weights when only using a few -const inter = Inter({ subsets: ['latin'] }) // Loads all weights - -// Good: Specify only needed weights (for non-variable fonts) -const inter = Inter({ subsets: ['latin'], weight: ['400', '700'] }) - -// Bad: Missing subset - loads all characters -const inter = Inter({}) - -// Good: Always specify subset -const inter = Inter({ subsets: ['latin'] }) -``` - -## Font in Specific Components - -```tsx -// For component-specific fonts, export from a shared file -// lib/fonts.ts -import { Inter, Playfair_Display } from 'next/font/google' - -export const inter = Inter({ subsets: ['latin'], variable: '--font-inter' }) -export const playfair = Playfair_Display({ subsets: ['latin'], variable: '--font-playfair' }) - -// components/Heading.tsx -import { playfair } from '@/lib/fonts' - -export function Heading({ children }) { - return

{children}

-} -``` - -### Cache Components (Next.js 16) - - -### File Level - -```tsx -'use cache' - -export default async function Page() { - // Entire page is cached - const data = await fetchData() - return
{data}
-} -``` - -### Component Level - -```tsx -export async function CachedComponent() { - 'use cache' - const data = await fetchData() - return
{data}
-} -``` - -### Function Level - -```tsx -export async function getData() { - 'use cache' - return db.query('SELECT * FROM posts') -} -``` - ---- - -### Optimistic UI for Server Actions - - -When Client Components need data: - -### Option 1: Pass from Server Component (Preferred) - -```tsx -// Server Component -async function Page() { - const data = await fetchData(); - return ; -} - -// Client Component -'use client'; -function ClientComponent({ initialData }) { - const [data, setData] = useState(initialData); - // ... -} -``` - -### Option 2: Fetch on Mount (When Necessary) - -```tsx -'use client'; -import { useEffect, useState } from 'react'; - -function ClientComponent() { - const [data, setData] = useState(null); - - useEffect(() => { - fetch('/api/data') - .then(r => r.json()) - .then(setData); - }, []); - - if (!data) return ; - return
{data.value}
; -} -``` - -### Option 3: Server Action for Reads (Works But Not Ideal) - -Server Actions can be called from Client Components for reads, but this is not their intended purpose: - -```tsx -'use client'; -import { getData } from './actions'; -import { useEffect, useState } from 'react'; - -function ClientComponent() { - const [data, setData] = useState(null); - - useEffect(() => { - getData().then(setData); - }, []); - - return
{data?.value}
; -} -``` - -**Note**: Server Actions always use POST, so no HTTP caching. Prefer Route Handlers for cacheable reads. - ---- - Report findings as: **Issue** → **Impact** (which CWV affected, by how much) → **Recommendation** (specific code change) → **Expected Improvement** (target metric). -Always reference the **Next.js skill** (`⤳ skill: nextjs`) for framework patterns. For monitoring setup, configure drains via Dashboard or REST API. +For monitoring setup, configure drains via Dashboard or REST API. diff --git a/agents/performance-optimizer.md.tmpl b/agents/performance-optimizer.md.tmpl index 8c26992..e798c1a 100644 --- a/agents/performance-optimizer.md.tmpl +++ b/agents/performance-optimizer.md.tmpl @@ -117,32 +117,6 @@ CLS > 0.1? --- -## Rendering Strategy Decision Tree - - -{{include:skill:nextjs:file:references/rsc-boundaries.md}} - ---- - -## Bundle Size Analysis - - -{{include:skill:nextjs:file:references/bundling.md:Bundle Analysis}} - ---- - -## Caching Strategy Matrix - - -{{include:skill:nextjs:file:references/data-patterns.md:Decision Tree}} - -### Cache Invalidation Patterns - - -{{include:skill:next-cache-components:Cache Invalidation}} - ---- - ## Performance Audit Checklist Run through this when asked to optimize a Vercel application: @@ -160,30 +134,6 @@ Run through this when asked to optimize a Vercel application: --- -## Specific Fix Patterns - -### Image Optimization - - -{{include:skill:nextjs:file:references/image.md}} - -### Font Loading - - -{{include:skill:nextjs:file:references/font.md}} - -### Cache Components (Next.js 16) - - -{{include:skill:next-cache-components:`use cache` Directive}} - -### Optimistic UI for Server Actions - - -{{include:skill:nextjs:file:references/data-patterns.md:Client Component Data Fetching}} - ---- - Report findings as: **Issue** → **Impact** (which CWV affected, by how much) → **Recommendation** (specific code change) → **Expected Improvement** (target metric). -Always reference the **Next.js skill** (`⤳ skill: nextjs`) for framework patterns. For monitoring setup, configure drains via Dashboard or REST API. +For monitoring setup, configure drains via Dashboard or REST API. diff --git a/generated/build-from-skills.manifest.json b/generated/build-from-skills.manifest.json index a07dbfe..ad1547c 100644 --- a/generated/build-from-skills.manifest.json +++ b/generated/build-from-skills.manifest.json @@ -1,6 +1,6 @@ { "version": 1, - "generatedAt": "2026-06-11T01:55:09.119Z", + "generatedAt": "2026-06-16T23:46:02.405Z", "templates": [ { "template": "agents/ai-architect.md.tmpl", @@ -28,68 +28,8 @@ { "template": "agents/performance-optimizer.md.tmpl", "output": "agents/performance-optimizer.md", - "dependencies": [ - "nextjs", - "next-cache-components" - ], - "includes": [ - { - "marker": "{{include:skill:nextjs:file:references/rsc-boundaries.md}}", - "skillName": "nextjs", - "target": "file:references/rsc-boundaries.md", - "type": "section", - "lineNumber": 123 - }, - { - "marker": "{{include:skill:nextjs:file:references/bundling.md:Bundle Analysis}}", - "skillName": "nextjs", - "target": "file:references/bundling.md:Bundle Analysis", - "type": "section", - "lineNumber": 130 - }, - { - "marker": "{{include:skill:nextjs:file:references/data-patterns.md:Decision Tree}}", - "skillName": "nextjs", - "target": "file:references/data-patterns.md:Decision Tree", - "type": "section", - "lineNumber": 137 - }, - { - "marker": "{{include:skill:next-cache-components:Cache Invalidation}}", - "skillName": "next-cache-components", - "target": "Cache Invalidation", - "type": "section", - "lineNumber": 142 - }, - { - "marker": "{{include:skill:nextjs:file:references/image.md}}", - "skillName": "nextjs", - "target": "file:references/image.md", - "type": "section", - "lineNumber": 168 - }, - { - "marker": "{{include:skill:nextjs:file:references/font.md}}", - "skillName": "nextjs", - "target": "file:references/font.md", - "type": "section", - "lineNumber": 173 - }, - { - "marker": "{{include:skill:next-cache-components:`use cache` Directive}}", - "skillName": "next-cache-components", - "target": "`use cache` Directive", - "type": "section", - "lineNumber": 178 - }, - { - "marker": "{{include:skill:nextjs:file:references/data-patterns.md:Client Component Data Fetching}}", - "skillName": "nextjs", - "target": "file:references/data-patterns.md:Client Component Data Fetching", - "type": "section", - "lineNumber": 183 - } - ] + "dependencies": [], + "includes": [] }, { "template": "agents/deployment-expert.md.tmpl", diff --git a/generated/skill-catalog.md b/generated/skill-catalog.md index bc3412f..3e643d0 100644 --- a/generated/skill-catalog.md +++ b/generated/skill-catalog.md @@ -1,8 +1,8 @@ # Skill Catalog > Auto-generated by `scripts/generate-catalog.ts` — do not edit manually. -> Generated: 2026-06-09T19:40:07.166Z -> Skills: 28 +> Generated: 2026-06-16T23:46:02.431Z +> Skills: 24 ## Table of Contents @@ -29,16 +29,12 @@ | `verification` | 7 | 0 | 8 | 0 | | `auth` | 6 | 14 | 12 | 0 | | `deployments-cicd` | 6 | 6 | 7 | 0 | -| `next-cache-components` | 6 | 5 | 1 | 1 | | `next-forge` | 6 | 32 | 8 | 21 | -| `next-upgrade` | 6 | 2 | 5 | 0 | | `routing-middleware` | 6 | 20 | 1 | 0 | | `runtime-cache` | 6 | 4 | 4 | 0 | | `shadcn` | 6 | 7 | 6 | 0 | -| `nextjs` | 5 | 15 | 7 | 0 | -| `vercel-connect` | 5 | 0 | 6 | 3 | +| `vercel-connect` | 5 | 2 | 6 | 4 | | `react-best-practices` | 4 | 8 | 0 | 2 | -| `turbopack` | 4 | 1 | 2 | 0 | | `vercel-agent` | 4 | 6 | 1 | 0 | | `vercel-cli` | 4 | 5 | 7 | 0 | | `vercel-sandbox` | 4 | 0 | 4 | 1 | @@ -521,34 +517,6 @@ - `vercel deploy` (bash) - `vercel build` (bash) -#### `next-cache-components` (priority 6) - -**Path patterns:** -- `next.config.*` -- `app/**` -- `src/app/**` -- `apps/*/app/**` -- `apps/*/src/app/**` - -**Bash patterns:** -- `\bnext\s+(dev|build)\b` - -**Import patterns:** -- `next/cache` - -**Matched examples:** -- `next.config.ts` (path) -- `next.config.js` (path) -- `next.config.mjs` (path) -- `app/api/chat/route.ts` (path) -- `app/api/completion/route.ts` (path) -- `app/layout.tsx` (path) -- `src/app/layout.tsx` (path) -- `app/api/bot/route.ts` (path) -- `next dev` (bash) -- `next build` (bash) -- `next dev --turbo` (bash) - #### `next-forge` (priority 6) **Path patterns:** @@ -621,24 +589,6 @@ **Matched examples:** - `apps/web/vercel.json` (path) -#### `next-upgrade` (priority 6) - -**Path patterns:** -- `next.config.*` -- `package.json` - -**Bash patterns:** -- `\bnpx\s+@next/codemod\b` -- `\bnpm\s+(install|i|add)\s+[^\n]*\bnext@` -- `\bpnpm\s+(install|i|add)\s+[^\n]*\bnext@` -- `\bbun\s+(install|i|add)\s+[^\n]*\bnext@` -- `\byarn\s+add\s+[^\n]*\bnext@` - -**Matched examples:** -- `next.config.ts` (path) -- `next.config.js` (path) -- `next.config.mjs` (path) - #### `routing-middleware` (priority 6) **Path patterns:** @@ -715,54 +665,11 @@ - `npx shadcn add button` (bash) - `npx create-next-app` (bash) -#### `nextjs` (priority 5) +#### `vercel-connect` (priority 5) **Path patterns:** -- `next.config.*` -- `next-env.d.ts` -- `app/**` -- `pages/**` -- `src/app/**` -- `src/pages/**` -- `tailwind.config.*` -- `postcss.config.*` -- `tsconfig.json` -- `tsconfig.*.json` -- `apps/*/app/**` -- `apps/*/pages/**` -- `apps/*/src/app/**` -- `apps/*/src/pages/**` -- `apps/*/next.config.*` - -**Bash patterns:** -- `\bnext\s+(dev|build|start|lint)\b` -- `\bnext\s+experimental-analyze\b` -- `\bnpx\s+create-next-app\b` -- `\bbunx\s+create-next-app\b` -- `\bnpm\s+run\s+(dev|build|start)\b` -- `\bpnpm\s+(dev|build)\b` -- `\bbun\s+run\s+(dev|build)\b` - -**Matched examples:** -- `next.config.ts` (path) -- `next.config.js` (path) -- `next.config.mjs` (path) -- `app/api/chat/route.ts` (path) -- `app/api/completion/route.ts` (path) -- `app/layout.tsx` (path) -- `src/app/layout.tsx` (path) -- `pages/api/auth/[...nextauth].ts` (path) -- `pages/api/hello.ts` (path) -- `app/api/bot/route.ts` (path) -- `next dev` (bash) -- `next build` (bash) -- `next dev --turbo` (bash) -- `npm run dev` (bash) -- `pnpm dev` (bash) -- `bun run dev` (bash) -- `npx create-next-app` (bash) - -#### `vercel-connect` (priority 5) +- `agent/connections/**` +- `agent/channels/**` **Bash patterns:** - `\bvercel\s+connect\b` @@ -774,6 +681,7 @@ **Import patterns:** - `@vercel/connect` +- `@vercel/connect/eve` - `@vercel/connect/authjs` - `@vercel/connect/betterauth` @@ -796,21 +704,6 @@ **Matched examples:** - `components/ui/button.tsx` (path) -#### `turbopack` (priority 4) - -**Path patterns:** -- `next.config.*` - -**Bash patterns:** -- `\bnext\s+dev\s+--turbo\b` -- `\bnext\s+dev\s+--turbopack\b` - -**Matched examples:** -- `next.config.ts` (path) -- `next.config.js` (path) -- `next.config.mjs` (path) -- `next dev --turbo` (bash) - #### `vercel-agent` (priority 4) **Path patterns:** @@ -893,18 +786,11 @@ Shows which skills compete on shared file targets. | `middleware.ts` | `auth` (6), `routing-middleware` (6) | | `middleware.js` | `auth` (6), `routing-middleware` (6) | | `src/middleware.ts` | `auth` (6), `routing-middleware` (6) | -| `next.config.ts` | `next-cache-components` (6), `next-upgrade` (6), `nextjs` (5), `turbopack` (4) | -| `next.config.js` | `next-cache-components` (6), `next-upgrade` (6), `nextjs` (5), `turbopack` (4) | -| `next.config.mjs` | `next-cache-components` (6), `next-upgrade` (6), `nextjs` (5), `turbopack` (4) | -| `app/api/chat/route.ts` | `ai-sdk` (8), `chat-sdk` (8), `vercel-functions` (8), `next-cache-components` (6), `nextjs` (5) | -| `app/api/completion/route.ts` | `ai-sdk` (8), `vercel-functions` (8), `next-cache-components` (6), `nextjs` (5) | -| `app/layout.tsx` | `next-cache-components` (6), `nextjs` (5) | -| `src/app/layout.tsx` | `next-cache-components` (6), `nextjs` (5) | -| `pages/api/auth/[...nextauth].ts` | `vercel-functions` (8), `nextjs` (5) | +| `app/api/chat/route.ts` | `ai-sdk` (8), `chat-sdk` (8), `vercel-functions` (8) | +| `app/api/completion/route.ts` | `ai-sdk` (8), `vercel-functions` (8) | | `components/ui/button.tsx` | `shadcn` (6), `react-best-practices` (4) | | `.github/workflows/deploy.yml` | `deployments-cicd` (6), `vercel-agent` (4) | -| `pages/api/hello.ts` | `vercel-functions` (8), `nextjs` (5) | -| `app/api/bot/route.ts` | `chat-sdk` (8), `vercel-functions` (8), `next-cache-components` (6), `nextjs` (5) | +| `app/api/bot/route.ts` | `chat-sdk` (8), `vercel-functions` (8) | ## Bash Overlap Matrix @@ -918,13 +804,6 @@ Shows which skills compete on shared bash commands. | `vercel env add` | `env-vars` (7), `vercel-cli` (4) | | `vercel logs` | `vercel-functions` (8), `vercel-cli` (4) | | `vercel build` | `deployments-cicd` (6), `vercel-cli` (4) | -| `next dev` | `verification` (7), `next-cache-components` (6), `nextjs` (5) | -| `next build` | `next-cache-components` (6), `nextjs` (5) | -| `next dev --turbo` | `verification` (7), `next-cache-components` (6), `nextjs` (5), `turbopack` (4) | -| `npm run dev` | `verification` (7), `nextjs` (5) | -| `pnpm dev` | `verification` (7), `nextjs` (5) | -| `bun run dev` | `verification` (7), `nextjs` (5) | -| `npx create-next-app` | `shadcn` (6), `nextjs` (5) | | `vercel integration add` | `vercel-cli` (4), `marketplace` (3) | | `vercel firewall` | `vercel-firewall` (7), `vercel-cli` (4) | @@ -938,11 +817,11 @@ Shows which skills compete on shared bash commands. **Priority 7:** `ai-gateway`, `env-vars`, `microfrontends`, `vercel-firewall`, `vercel-storage`, `verification` -**Priority 6:** `auth`, `deployments-cicd`, `next-cache-components`, `next-forge`, `next-upgrade`, `routing-middleware`, `runtime-cache`, `shadcn` +**Priority 6:** `auth`, `deployments-cicd`, `next-forge`, `routing-middleware`, `runtime-cache`, `shadcn` -**Priority 5:** `nextjs`, `vercel-connect` +**Priority 5:** `vercel-connect` -**Priority 4:** `react-best-practices`, `turbopack`, `vercel-agent`, `vercel-cli`, `vercel-sandbox` +**Priority 4:** `react-best-practices`, `vercel-agent`, `vercel-cli`, `vercel-sandbox` **Priority 3:** `marketplace` diff --git a/generated/skill-manifest.json b/generated/skill-manifest.json index 3245285..5b91c9c 100644 --- a/generated/skill-manifest.json +++ b/generated/skill-manifest.json @@ -1,5 +1,5 @@ { - "generatedAt": "2026-06-11T01:55:09.087Z", + "generatedAt": "2026-06-16T23:46:02.375Z", "version": 2, "skills": { "vercel-agent": { @@ -125,29 +125,12 @@ "\\bnpx\\s+@vercel/config\\b" ], "importRegexSources": [], - "validate": [ - { - "pattern": "NextResponse.*from\\s+[''\"]next/server[''\"]|from\\s+[''\"]next/server[''\"].*NextResponse", - "message": "Next.js middleware.ts is renamed to proxy.ts in Next.js 16 — rename the file and use the Node.js runtime. Run Skill(nextjs) for proxy.ts migration guidance.", - "severity": "recommended", - "skipIfFileContains": "proxy\\.ts|runtime.*nodejs", - "upgradeToSkill": "nextjs", - "upgradeWhy": "Guides migration from middleware.ts to proxy.ts with correct file placement, Node.js runtime, and Next.js 16 patterns.", - "upgradeMode": "soft" - } - ], "chainTo": [ { "pattern": "from\\s+[''\"\"]next-auth[''\"\"]", "targetSkill": "auth", "message": "Auth logic in middleware — loading Auth guidance for Clerk/Auth0 integration patterns." }, - { - "pattern": "NextResponse.*from\\s+[''\"]next/server[''\"]|from\\s+[''\"]next/server[''\"].*NextResponse", - "targetSkill": "nextjs", - "message": "middleware.ts with next/server imports detected — loading Next.js guidance for proxy.ts migration (Next.js 16 renames middleware.ts to proxy.ts with Node.js runtime).", - "skipIfFileContains": "proxy\\.ts|runtime.*nodejs" - }, { "pattern": "from\\s+[''\"\"](jsonwebtoken)[''\"\"]|jwt\\.(verify|decode)\\(", "targetSkill": "auth", @@ -462,22 +445,12 @@ } ], "chainTo": [ - { - "pattern": "from\\\\s+['\\\"]@vercel/postgres['\\\"]", - "targetSkill": "nextjs", - "message": "@vercel/postgres is sunset — loading Next.js guidance for integrating @neondatabase/serverless with App Router." - }, { "pattern": "@vercel/postgres", "targetSkill": "vercel-storage", "message": "@vercel/postgres is sunset — migrate to @neondatabase/serverless. Run `vercel integration add neon` for one-click Marketplace provisioning with unified billing.", "skipIfFileContains": "@neondatabase/serverless|from\\\\s+['\\\"]@neondatabase" }, - { - "pattern": "from\\\\s+['\\\"]@vercel/kv['\\\"]", - "targetSkill": "nextjs", - "message": "@vercel/kv is sunset — loading Next.js guidance for integrating @upstash/redis with App Router." - }, { "pattern": "createPool\\\\s*\\\\(|from\\\\s+['\\\"]@vercel/postgres/pool['\\\"]", "targetSkill": "vercel-storage", @@ -1203,65 +1176,6 @@ ] } }, - "turbopack": { - "priority": 4, - "summary": "", - "docs": [ - "https://turbo.build/pack/docs", - "https://nextjs.org/docs/architecture/turbopack" - ], - "sitemap": "https://turbo.build/sitemap.xml", - "pathPatterns": [ - "next.config.*" - ], - "bashPatterns": [ - "\\bnext\\s+dev\\s+--turbo\\b", - "\\bnext\\s+dev\\s+--turbopack\\b" - ], - "importPatterns": [], - "bodyPath": "skills/turbopack/SKILL.md", - "pathRegexSources": [ - "^next\\.config\\.[^/]*$" - ], - "bashRegexSources": [ - "\\bnext\\s+dev\\s+--turbo\\b", - "\\bnext\\s+dev\\s+--turbopack\\b" - ], - "importRegexSources": [], - "chainTo": [ - { - "pattern": "webpack\\s*:\\s*\\(|webpack\\s*\\(config", - "targetSkill": "nextjs", - "message": "Webpack config detected — loading Next.js guidance for migrating webpack customizations to Turbopack top-level config in Next.js 16." - }, - { - "pattern": "turbopack\\s*:\\s*\\{|experimental\\.turbopack", - "targetSkill": "nextjs", - "message": "Turbopack configuration detected — loading Next.js guidance for top-level turbopack config syntax in Next.js 16 (moved from experimental.turbopack)." - } - ], - "retrieval": { - "aliases": [ - "next bundler", - "turbopack", - "fast bundler", - "hmr" - ], - "intents": [ - "enable turbopack", - "fix build issue", - "speed up dev server", - "configure bundler" - ], - "entities": [ - "Turbopack", - "HMR", - "bundler", - "next dev --turbopack" - ], - "examples": [] - } - }, "marketplace": { "priority": 3, "summary": "", @@ -2610,391 +2524,6 @@ "examples": [] } }, - "nextjs": { - "priority": 5, - "summary": "", - "docs": [ - "https://nextjs.org/docs", - "https://nextjs.org/docs/app" - ], - "sitemap": "https://nextjs.org/sitemap.xml", - "pathPatterns": [ - "next.config.*", - "next-env.d.ts", - "app/**", - "pages/**", - "src/app/**", - "src/pages/**", - "tailwind.config.*", - "postcss.config.*", - "tsconfig.json", - "tsconfig.*.json", - "apps/*/app/**", - "apps/*/pages/**", - "apps/*/src/app/**", - "apps/*/src/pages/**", - "apps/*/next.config.*" - ], - "bashPatterns": [ - "\\bnext\\s+(dev|build|start|lint)\\b", - "\\bnext\\s+experimental-analyze\\b", - "\\bnpx\\s+create-next-app\\b", - "\\bbunx\\s+create-next-app\\b", - "\\bnpm\\s+run\\s+(dev|build|start)\\b", - "\\bpnpm\\s+(dev|build)\\b", - "\\bbun\\s+run\\s+(dev|build)\\b" - ], - "importPatterns": [], - "bodyPath": "skills/nextjs/SKILL.md", - "pathRegexSources": [ - "^next\\.config\\.[^/]*$", - "^next-env\\.d\\.ts$", - "^app\\/.*$", - "^pages\\/.*$", - "^src\\/app\\/.*$", - "^src\\/pages\\/.*$", - "^tailwind\\.config\\.[^/]*$", - "^postcss\\.config\\.[^/]*$", - "^tsconfig\\.json$", - "^tsconfig\\.[^/]*\\.json$", - "^apps\\/[^/]*\\/app\\/.*$", - "^apps\\/[^/]*\\/pages\\/.*$", - "^apps\\/[^/]*\\/src\\/app\\/.*$", - "^apps\\/[^/]*\\/src\\/pages\\/.*$", - "^apps\\/[^/]*\\/next\\.config\\.[^/]*$" - ], - "bashRegexSources": [ - "\\bnext\\s+(dev|build|start|lint)\\b", - "\\bnext\\s+experimental-analyze\\b", - "\\bnpx\\s+create-next-app\\b", - "\\bbunx\\s+create-next-app\\b", - "\\bnpm\\s+run\\s+(dev|build|start)\\b", - "\\bpnpm\\s+(dev|build)\\b", - "\\bbun\\s+run\\s+(dev|build)\\b" - ], - "importRegexSources": [], - "validate": [ - { - "pattern": "export.*getServerSideProps", - "message": "getServerSideProps is removed in App Router — use server components or route handlers", - "severity": "error", - "upgradeToSkill": "nextjs", - "upgradeWhy": "Guides migration from Pages Router getServerSideProps to App Router server components with async data fetching.", - "upgradeMode": "soft" - }, - { - "pattern": "getServerSideProps", - "message": "getServerSideProps is a Pages Router pattern — migrate to App Router server components", - "severity": "warn" - }, - { - "pattern": "export.*getStaticProps", - "message": "getStaticProps is removed in App Router — use generateStaticParams + server components instead", - "severity": "error", - "upgradeToSkill": "nextjs", - "upgradeWhy": "Guides migration from Pages Router getStaticProps to App Router generateStaticParams with server components.", - "upgradeMode": "soft" - }, - { - "pattern": "getStaticProps", - "message": "getStaticProps is a Pages Router pattern — migrate to App Router generateStaticParams + server components", - "severity": "warn" - }, - { - "pattern": "from\\s+['\"]next/router['\"]", - "message": "next/router is Pages Router only — use next/navigation for App Router", - "severity": "error", - "upgradeToSkill": "nextjs", - "upgradeWhy": "Guides migration from next/router to next/navigation with useRouter, usePathname, useSearchParams hooks.", - "upgradeMode": "soft" - }, - { - "pattern": "(useState|useEffect)", - "message": "React hooks require \"use client\" directive — add it at the top of client components", - "severity": "warn", - "skipIfFileContains": "^['\\\"]use client['\\\"]" - }, - { - "pattern": "from\\s+['\"]next/head['\"]", - "message": "next/head is Pages Router — use export const metadata or generateMetadata() in App Router. Run Skill(nextjs) for metadata API guidance.", - "severity": "error", - "skipIfFileContains": "export\\s+(const\\s+)?metadata|generateMetadata", - "upgradeToSkill": "nextjs", - "upgradeWhy": "Guides migration from next/head to the App Router metadata API (export const metadata / generateMetadata()).", - "upgradeMode": "soft" - }, - { - "pattern": "export\\s+(default\\s+)?function\\s+middleware", - "message": "middleware() is renamed to proxy() in Next.js 16 — rename the function and the file to proxy.ts. Run Skill(routing-middleware) for proxy.ts migration guidance.", - "severity": "recommended", - "upgradeToSkill": "routing-middleware", - "upgradeWhy": "Guides migration from middleware.ts to proxy.ts with correct file placement, runtime config, and request interception patterns.", - "upgradeMode": "soft" - }, - { - "pattern": "revalidateTag\\(\\s*['\"][^'\"]+['\"]\\s*\\)", - "message": "Single-arg revalidateTag(tag) is deprecated in Next.js 16 — pass a cacheLife profile: revalidateTag(tag, \"max\")", - "severity": "recommended", - "upgradeToSkill": "nextjs", - "upgradeWhy": "Guides migration from single-arg revalidateTag to the Next.js 16 two-arg API with cacheLife profiles.", - "upgradeMode": "soft" - }, - { - "pattern": "\\bcacheHandler\\s*:", - "message": "Singular cacheHandler is deprecated in Next.js 16 — use cacheHandlers (plural) with per-type handlers", - "severity": "recommended", - "upgradeToSkill": "nextjs", - "upgradeWhy": "Guides migration from singular cacheHandler to Next.js 16 cacheHandlers (plural) with per-type handler config.", - "upgradeMode": "soft" - }, - { - "pattern": "useRef\\(\\s*\\)", - "message": "useRef() requires an initial value in React 19 — use useRef(null) or useRef(0)", - "severity": "error" - }, - { - "pattern": "next\\s+export", - "message": "next export was removed — use output: \"export\" in next.config.js for static export", - "severity": "error", - "upgradeToSkill": "nextjs", - "upgradeWhy": "Guides migration from next export CLI command to output: \"export\" in next.config for static site generation.", - "upgradeMode": "soft" - }, - { - "pattern": "(?]*fonts\\.googleapis", - "message": "External font loader detected. Use next/font for zero-CLS, self-hosted font loading with automatic optimization.", - "severity": "recommended", - "skipIfFileContains": "next/font", - "upgradeToSkill": "nextjs", - "upgradeWhy": "Guides migration from external font loaders to next/font with Geist Sans/Mono for zero-CLS font optimization.", - "upgradeMode": "soft" - } - ], - "chainTo": [ - { - "pattern": "export\\s+(default\\s+)?function\\s+middleware", - "targetSkill": "routing-middleware", - "message": "middleware() is renamed to proxy() in Next.js 16 — loading Routing Middleware guidance for proxy.ts migration." - }, - { - "pattern": "from\\\\s+['\\\"]@vercel/(postgres|kv)['\\\"]", - "targetSkill": "vercel-storage", - "message": "Sunset storage package detected — loading Vercel Storage guidance for Neon/Upstash migration." - }, - { - "pattern": "from\\\\s+['\\\"]@ai-sdk/(anthropic|openai)['\\\"]", - "targetSkill": "ai-gateway", - "message": "Direct AI provider SDK import — loading AI Gateway guidance for unified model routing with failover and cost tracking." - }, - { - "pattern": "from\\s+[''\"\"](next-auth|@auth/core)[''\"\"]|NextAuth\\(|getServerSession\\(", - "targetSkill": "auth", - "message": "Legacy auth pattern detected — loading managed authentication guidance (Clerk, Descope, Auth0)." - }, - { - "pattern": "NextApiRequest|NextApiResponse|export\\s+default\\s+function\\s+handler", - "targetSkill": "vercel-functions", - "message": "Pages Router API handler detected — loading Vercel Functions guidance for App Router migration." - }, - { - "pattern": "from\\s+[''\"\"](lru-cache|node-cache|memory-cache)[''\"\"]|new\\s+(LRUCache|NodeCache)\\(", - "targetSkill": "runtime-cache", - "message": "In-process cache detected — loading Runtime Cache guidance for serverless-compatible caching." - }, - { - "pattern": "fetch\\s*\\(\\s*[''\"\"](https?://)?(api\\.openai\\.com|api\\.anthropic\\.com|api\\.cohere\\.ai)", - "targetSkill": "ai-gateway", - "message": "Raw AI provider fetch URL detected — loading AI Gateway guidance for unified routing, failover, and OIDC auth.", - "skipIfFileContains": "@ai-sdk/|from\\s+[''\"\"](ai)[''\"\"]|ai-gateway|gateway\\(" - }, - { - "pattern": "jwt\\.(sign|verify|decode)\\(|from\\s+[''\"\"](jsonwebtoken)[''\"\"]|new\\s+SignJWT\\(|jwtVerify\\(", - "targetSkill": "auth", - "message": "Manual JWT token handling detected — loading Auth guidance for managed authentication (Clerk, Descope, Auth0).", - "skipIfFileContains": "clerkMiddleware|@clerk/|@auth0/|@descope/|from\\s+[''\"\"](next-auth)[''\"\"]" - }, - { - "pattern": "from\\s+[''\"]@/components/ui/|from\\s+[''\"]@/components/ui[''\"\"]", - "targetSkill": "shadcn", - "message": "shadcn/ui component imports detected — loading shadcn guidance for component composition, theming, and registry patterns.", - "skipIfFileContains": "shadcn|components\\.json" - }, - { - "pattern": "from\\s+[''\"\"](styled-components|@emotion/(react|styled)|@mui/material)[''\"\"]", - "targetSkill": "shadcn", - "message": "CSS-in-JS library detected — loading shadcn/ui guidance for Tailwind CSS + Radix UI component patterns (Vercel recommended).", - "skipIfFileContains": "@/components/ui|shadcn" - }, - { - "pattern": "getInitialProps", - "targetSkill": "nextjs", - "message": "getInitialProps is a legacy Pages Router pattern — loading Next.js guidance for App Router migration with server components and async data fetching.", - "skipIfFileContains": "app/.*page\\.|generateStaticParams|use cache" - }, - { - "pattern": "export.*getServerSideProps|getServerSideProps\\s*\\(", - "targetSkill": "nextjs", - "message": "getServerSideProps is a Pages Router pattern — loading Next.js guidance for App Router migration with server components and async data fetching.", - "skipIfFileContains": "generateStaticParams|use cache|app/.*page\\." - }, - { - "pattern": "export.*getStaticProps|getStaticProps\\s*\\(", - "targetSkill": "nextjs", - "message": "getStaticProps is a Pages Router pattern — loading Next.js guidance for App Router migration with generateStaticParams and server components.", - "skipIfFileContains": "generateStaticParams|use cache|app/.*page\\." - }, - { - "pattern": "_app\\.(tsx?|jsx?)", - "targetSkill": "nextjs", - "message": "_app.tsx is a Pages Router pattern — loading Next.js guidance for App Router layout.tsx migration.", - "skipIfFileContains": "app/layout\\.|app/.*layout\\." - }, - { - "pattern": "_document\\.(tsx?|jsx?)", - "targetSkill": "nextjs", - "message": "_document.tsx is a Pages Router pattern — loading Next.js guidance for App Router layout.tsx with metadata API migration.", - "skipIfFileContains": "app/layout\\.|app/.*layout\\." - }, - { - "pattern": "from\\\\s+['\\\"]next/document['\\\"]", - "targetSkill": "nextjs", - "message": "next/document is Pages Router only — loading Next.js guidance for App Router layout.tsx with html/body structure.", - "skipIfFileContains": "app/layout\\.|app/.*layout\\." - }, - { - "pattern": "pages/api/", - "targetSkill": "vercel-functions", - "message": "Pages Router API route (pages/api/) detected — loading Vercel Functions guidance for App Router route handlers with named HTTP exports.", - "skipIfFileContains": "export\\s+(async\\s+)?function\\s+(GET|POST|PUT|PATCH|DELETE)" - } - ], - "promptSignals": { - "phrases": [ - "next.js", - "nextjs", - "app router", - "server component", - "server action" - ], - "allOf": [ - [ - "middleware", - "next" - ], - [ - "layout", - "route" - ] - ], - "anyOf": [ - "pages router", - "getserversideprops", - "use server" - ], - "noneOf": [], - "minScore": 6 - }, - "retrieval": { - "aliases": [ - "next.js", - "nextjs app", - "react framework", - "app router" - ], - "intents": [ - "set up routing and layouts in a Next.js app", - "choose between server and client components for a feature", - "configure data fetching or caching in App Router", - "add middleware or proxy logic to handle requests", - "set up server rendering for React pages", - "add a new page with dynamic route segments" - ], - "entities": [ - "App Router", - "Server Components", - "Server Actions", - "generateMetadata", - "layout", - "proxy", - "next.config" - ], - "examples": [ - "add a new page with dynamic routing", - "should this be a server or client component", - "set up middleware for auth redirects", - "configure caching for this data fetch", - "set up server rendering for my pages" - ] - } - }, "microfrontends": { "priority": 7, "summary": "", @@ -3271,98 +2800,6 @@ "examples": [] } }, - "next-upgrade": { - "priority": 6, - "summary": "", - "docs": [ - "https://nextjs.org/docs/app/guides/upgrading", - "https://nextjs.org/docs/app/guides/upgrading/codemods" - ], - "pathPatterns": [ - "next.config.*", - "package.json" - ], - "bashPatterns": [ - "\\bnpx\\s+@next/codemod\\b", - "\\bnpm\\s+(install|i|add)\\s+[^\\n]*\\bnext@", - "\\bpnpm\\s+(install|i|add)\\s+[^\\n]*\\bnext@", - "\\bbun\\s+(install|i|add)\\s+[^\\n]*\\bnext@", - "\\byarn\\s+add\\s+[^\\n]*\\bnext@" - ], - "importPatterns": [], - "bodyPath": "skills/next-upgrade/SKILL.md", - "pathRegexSources": [ - "^next\\.config\\.[^/]*$", - "^package\\.json$" - ], - "bashRegexSources": [ - "\\bnpx\\s+@next/codemod\\b", - "\\bnpm\\s+(install|i|add)\\s+[^\\n]*\\bnext@", - "\\bpnpm\\s+(install|i|add)\\s+[^\\n]*\\bnext@", - "\\bbun\\s+(install|i|add)\\s+[^\\n]*\\bnext@", - "\\byarn\\s+add\\s+[^\\n]*\\bnext@" - ], - "importRegexSources": [], - "chainTo": [ - { - "pattern": "getServerSideProps|getStaticProps|next/router|next/head|next/document", - "targetSkill": "nextjs", - "message": "Pages Router patterns detected during upgrade — loading Next.js best practices for App Router migration." - } - ], - "promptSignals": { - "phrases": [ - "upgrade next", - "upgrade nextjs", - "migrate next", - "update next.js", - "next.js upgrade", - "nextjs migration", - "next codemod" - ], - "allOf": [ - [ - "upgrade", - "next" - ], - [ - "migrate", - "next" - ], - [ - "update", - "nextjs" - ] - ], - "anyOf": [ - "breaking changes", - "codemod", - "migration guide", - "version upgrade" - ], - "noneOf": [], - "minScore": 6 - }, - "retrieval": { - "aliases": [ - "next upgrade", - "nextjs migration", - "next codemod" - ], - "intents": [ - "upgrade Next.js to latest version", - "run Next.js codemods", - "migrate between major Next.js versions" - ], - "entities": [ - "codemod", - "migration", - "upgrade", - "breaking changes" - ], - "examples": [] - } - }, "chat-sdk": { "priority": 8, "summary": "", @@ -4041,109 +3478,6 @@ ] } }, - "next-cache-components": { - "priority": 6, - "summary": "", - "docs": [ - "https://nextjs.org/docs/app/getting-started/cache-components", - "https://nextjs.org/docs/app/api-reference/directives/use-cache" - ], - "pathPatterns": [ - "next.config.*", - "app/**", - "src/app/**", - "apps/*/app/**", - "apps/*/src/app/**" - ], - "bashPatterns": [ - "\\bnext\\s+(dev|build)\\b" - ], - "importPatterns": [ - "next/cache" - ], - "bodyPath": "skills/next-cache-components/SKILL.md", - "pathRegexSources": [ - "^next\\.config\\.[^/]*$", - "^app\\/.*$", - "^src\\/app\\/.*$", - "^apps\\/[^/]*\\/app\\/.*$", - "^apps\\/[^/]*\\/src\\/app\\/.*$" - ], - "bashRegexSources": [ - "\\bnext\\s+(dev|build)\\b" - ], - "importRegexSources": [ - { - "source": "(?:from\\s+|require\\s*\\(\\s*|import\\s*\\(\\s*)['\"]next\\/cache(?:\\/[^'\"]*)?['\"]", - "flags": "m" - } - ], - "chainTo": [ - { - "pattern": "use cache", - "targetSkill": "nextjs", - "message": "Cache component detected — loading Next.js best practices for RSC boundaries and data patterns alongside caching.", - "skipIfFileContains": "next-best-practices" - } - ], - "promptSignals": { - "phrases": [ - "use cache", - "cache components", - "partial prerendering", - "PPR", - "cacheLife", - "cacheTag", - "updateTag", - "unstable_cache" - ], - "allOf": [ - [ - "cache", - "component" - ], - [ - "cache", - "directive" - ], - [ - "partial", - "prerender" - ] - ], - "anyOf": [ - "revalidateTag", - "stale", - "revalidate", - "cache profile" - ], - "noneOf": [], - "minScore": 6 - }, - "retrieval": { - "aliases": [ - "cache components", - "partial prerendering", - "PPR", - "use cache" - ], - "intents": [ - "enable partial prerendering in Next.js", - "cache async data with use cache directive", - "invalidate cache with cacheTag", - "migrate from unstable_cache" - ], - "entities": [ - "use cache", - "cacheLife", - "cacheTag", - "updateTag", - "revalidateTag", - "PPR" - ], - "examples": [] - } - }, "env-vars": { "priority": 7, "summary": "", diff --git a/hooks/session-start-profiler.mjs b/hooks/session-start-profiler.mjs index ab637be..80c6ba0 100644 --- a/hooks/session-start-profiler.mjs +++ b/hooks/session-start-profiler.mjs @@ -20,10 +20,6 @@ import { hasSessionStartActivationMarkers } from "./session-start-activation.mjs import { buildSkillMap } from "./skill-map-frontmatter.mjs"; import { refreshActiveSessionMarker, trackDauActiveToday } from "./telemetry.mjs"; var FILE_MARKERS = [ - { file: "next.config.js", skills: ["nextjs", "turbopack"] }, - { file: "next.config.mjs", skills: ["nextjs", "turbopack"] }, - { file: "next.config.ts", skills: ["nextjs", "turbopack"] }, - { file: "next.config.mts", skills: ["nextjs", "turbopack"] }, { file: "vercel.json", skills: ["vercel-cli", "deployments-cicd", "vercel-functions"] }, { file: "middleware.ts", skills: ["routing-middleware"] }, { file: "middleware.js", skills: ["routing-middleware"] }, @@ -31,7 +27,6 @@ var FILE_MARKERS = [ { file: ".env.local", skills: ["env-vars"] } ]; var PACKAGE_MARKERS = { - "next": ["nextjs"], "ai": ["ai-sdk"], "@ai-sdk/openai": ["ai-sdk"], "@ai-sdk/anthropic": ["ai-sdk"], @@ -74,7 +69,6 @@ var SETUP_RESOURCE_DEPENDENCIES = { }; var SETUP_MODE_THRESHOLD = 3; var GREENFIELD_DEFAULT_SKILLS = [ - "nextjs", "ai-sdk", "vercel-cli", "env-vars" diff --git a/hooks/src/session-start-profiler.mts b/hooks/src/session-start-profiler.mts index 45f7c54..47ffb60 100644 --- a/hooks/src/session-start-profiler.mts +++ b/hooks/src/session-start-profiler.mts @@ -68,10 +68,6 @@ interface GreenfieldResult { * Mapping from marker file / condition to skill slugs. */ const FILE_MARKERS: FileMarker[] = [ - { file: "next.config.js", skills: ["nextjs", "turbopack"] }, - { file: "next.config.mjs", skills: ["nextjs", "turbopack"] }, - { file: "next.config.ts", skills: ["nextjs", "turbopack"] }, - { file: "next.config.mts", skills: ["nextjs", "turbopack"] }, { file: "vercel.json", skills: ["vercel-cli", "deployments-cicd", "vercel-functions"] }, { file: "middleware.ts", skills: ["routing-middleware"] }, { file: "middleware.js", skills: ["routing-middleware"] }, @@ -83,7 +79,6 @@ const FILE_MARKERS: FileMarker[] = [ * Dependency names in package.json -> skill slugs. */ const PACKAGE_MARKERS: Record = { - "next": ["nextjs"], "ai": ["ai-sdk"], "@ai-sdk/openai": ["ai-sdk"], "@ai-sdk/anthropic": ["ai-sdk"], @@ -131,7 +126,6 @@ const SETUP_RESOURCE_DEPENDENCIES: Record = { const SETUP_MODE_THRESHOLD = 3; const GREENFIELD_DEFAULT_SKILLS: string[] = [ - "nextjs", "ai-sdk", "vercel-cli", "env-vars", diff --git a/hooks/src/vercel-context.mts b/hooks/src/vercel-context.mts index 467ce90..2f5e309 100644 --- a/hooks/src/vercel-context.mts +++ b/hooks/src/vercel-context.mts @@ -31,10 +31,6 @@ interface ManagedContextChunkOptions { } const SKILL_TO_CHUNK: Record = { - "nextjs": { chunkId: "nextjs-platform", heading: "Next.js and Rendering" }, - "next-cache-components": { chunkId: "nextjs-platform", heading: "Next.js and Rendering" }, - "next-upgrade": { chunkId: "nextjs-platform", heading: "Next.js and Rendering" }, - "turbopack": { chunkId: "nextjs-platform", heading: "Next.js and Rendering" }, "next-forge": { chunkId: "nextjs-platform", heading: "Next.js and Rendering" }, "ai-sdk": { chunkId: "ai-stack", heading: "AI Stack" }, "ai-gateway": { chunkId: "ai-stack", heading: "AI Stack" }, diff --git a/hooks/vercel-context.mjs b/hooks/vercel-context.mjs index 114047c..af23c2d 100644 --- a/hooks/vercel-context.mjs +++ b/hooks/vercel-context.mjs @@ -10,10 +10,6 @@ var PLUGIN_ROOT = resolvePluginRoot(); var DEFAULT_CONTEXT_CHUNK_BUDGET_BYTES = 1800; var CONTEXT_CHUNK_KIND = "seen-context-chunks"; var SKILL_TO_CHUNK = { - "nextjs": { chunkId: "nextjs-platform", heading: "Next.js and Rendering" }, - "next-cache-components": { chunkId: "nextjs-platform", heading: "Next.js and Rendering" }, - "next-upgrade": { chunkId: "nextjs-platform", heading: "Next.js and Rendering" }, - "turbopack": { chunkId: "nextjs-platform", heading: "Next.js and Rendering" }, "next-forge": { chunkId: "nextjs-platform", heading: "Next.js and Rendering" }, "ai-sdk": { chunkId: "ai-stack", heading: "AI Stack" }, "ai-gateway": { chunkId: "ai-stack", heading: "AI Stack" }, diff --git a/skills/next-cache-components/SKILL.md b/skills/next-cache-components/SKILL.md deleted file mode 100644 index 26e5ae3..0000000 --- a/skills/next-cache-components/SKILL.md +++ /dev/null @@ -1,487 +0,0 @@ ---- -name: next-cache-components -description: Next.js 16 Cache Components guidance — PPR, use cache directive, cacheLife, cacheTag, updateTag, and migration from unstable_cache. Use when implementing partial prerendering, caching strategies, or migrating from older Next.js cache patterns. -metadata: - priority: 6 - docs: - - "https://nextjs.org/docs/app/getting-started/cache-components" - - "https://nextjs.org/docs/app/api-reference/directives/use-cache" - pathPatterns: - - 'next.config.*' - - 'app/**' - - 'src/app/**' - - 'apps/*/app/**' - - 'apps/*/src/app/**' - importPatterns: - - "next/cache" - bashPatterns: - - '\bnext\s+(dev|build)\b' - promptSignals: - phrases: - - "use cache" - - "cache components" - - "partial prerendering" - - "PPR" - - "cacheLife" - - "cacheTag" - - "updateTag" - - "unstable_cache" - allOf: - - [cache, component] - - [cache, directive] - - [partial, prerender] - anyOf: - - "revalidateTag" - - "stale" - - "revalidate" - - "cache profile" - noneOf: [] - minScore: 6 - validate: - - - pattern: 'unstable_cache\s*\(' - message: 'unstable_cache is deprecated in Next.js 16 — use the "use cache" directive with cacheTag() and cacheLife() instead' - severity: recommended - upgradeToSkill: next-cache-components - upgradeWhy: 'Guides migration from unstable_cache to use cache directive with cacheTag and cacheLife.' - - - pattern: '\bcacheHandler\s*:' - message: 'Singular cacheHandler is deprecated in Next.js 16 — use cacheHandlers (plural) with per-type handlers' - severity: recommended - - - pattern: revalidateTag\(\s*['"][^'"]+['"]\s*\) - message: 'Single-arg revalidateTag(tag) is deprecated in Next.js 16 — pass a cacheLife profile: revalidateTag(tag, "max")' - severity: recommended -retrieval: - aliases: - - cache components - - partial prerendering - - PPR - - use cache - intents: - - enable partial prerendering in Next.js - - cache async data with use cache directive - - invalidate cache with cacheTag - - migrate from unstable_cache - entities: - - use cache - - cacheLife - - cacheTag - - updateTag - - revalidateTag - - PPR -chainTo: - - - pattern: 'use cache' - targetSkill: nextjs - message: 'Cache component detected — loading Next.js best practices for RSC boundaries and data patterns alongside caching.' - skipIfFileContains: 'next-best-practices' - ---- - -# Cache Components (Next.js 16+) - -Cache Components enable Partial Prerendering (PPR) - mix static, cached, and dynamic content in a single route. - -## Enable Cache Components - -```ts -// next.config.ts -import type { NextConfig } from 'next' - -const nextConfig: NextConfig = { - cacheComponents: true, -} - -export default nextConfig -``` - -This replaces the old `experimental.ppr` flag. - ---- - -## Three Content Types - -With Cache Components enabled, content falls into three categories: - -### 1. Static (Auto-Prerendered) - -Synchronous code, imports, pure computations - prerendered at build time: - -```tsx -export default function Page() { - return ( -
-

Our Blog

{/* Static - instant */} - -
- ) -} -``` - -### 2. Cached (`use cache`) - -Async data that doesn't need fresh fetches every request: - -```tsx -async function BlogPosts() { - 'use cache' - cacheLife('hours') - - const posts = await db.posts.findMany() - return -} -``` - -### 3. Dynamic (Suspense) - -Runtime data that must be fresh - wrap in Suspense: - -```tsx -import { Suspense } from 'react' - -export default function Page() { - return ( - <> - {/* Cached */} - - Loading...

}> - {/* Dynamic - streams in */} -
- - ) -} - -async function UserPreferences() { - const theme = (await cookies()).get('theme')?.value - return

Theme: {theme}

-} -``` - ---- - -## `use cache` Directive - -### File Level - -```tsx -'use cache' - -export default async function Page() { - // Entire page is cached - const data = await fetchData() - return
{data}
-} -``` - -### Component Level - -```tsx -export async function CachedComponent() { - 'use cache' - const data = await fetchData() - return
{data}
-} -``` - -### Function Level - -```tsx -export async function getData() { - 'use cache' - return db.query('SELECT * FROM posts') -} -``` - ---- - -## Cache Profiles - -### Built-in Profiles - -```tsx -'use cache' // Default: 5m stale, 15m revalidate -``` - -```tsx -'use cache: remote' // Platform-provided cache (Redis, KV) -``` - -```tsx -'use cache: private' // For compliance, allows runtime APIs -``` - -### `cacheLife()` - Custom Lifetime - -```tsx -import { cacheLife } from 'next/cache' - -async function getData() { - 'use cache' - cacheLife('hours') // Built-in profile - return fetch('/api/data') -} -``` - -Built-in profiles: `'default'`, `'minutes'`, `'hours'`, `'days'`, `'weeks'`, `'max'` - -### Inline Configuration - -```tsx -async function getData() { - 'use cache' - cacheLife({ - stale: 3600, // 1 hour - serve stale while revalidating - revalidate: 7200, // 2 hours - background revalidation interval - expire: 86400, // 1 day - hard expiration - }) - return fetch('/api/data') -} -``` - ---- - -## Cache Invalidation - -### `cacheTag()` - Tag Cached Content - -```tsx -import { cacheTag } from 'next/cache' - -async function getProducts() { - 'use cache' - cacheTag('products') - return db.products.findMany() -} - -async function getProduct(id: string) { - 'use cache' - cacheTag('products', `product-${id}`) - return db.products.findUnique({ where: { id } }) -} -``` - -### `updateTag()` - Immediate Invalidation - -Use when you need the cache refreshed within the same request: - -```tsx -'use server' - -import { updateTag } from 'next/cache' - -export async function updateProduct(id: string, data: FormData) { - await db.products.update({ where: { id }, data }) - updateTag(`product-${id}`) // Immediate - same request sees fresh data -} -``` - -### `revalidateTag()` - Background Revalidation - -Use for stale-while-revalidate behavior: - -```tsx -'use server' - -import { revalidateTag } from 'next/cache' - -export async function createPost(data: FormData) { - await db.posts.create({ data }) - revalidateTag('posts') // Background - next request sees fresh data -} -``` - ---- - -## Runtime Data Constraint - -**Cannot** access `cookies()`, `headers()`, or `searchParams` inside `use cache`. - -### Solution: Pass as Arguments - -```tsx -// Wrong - runtime API inside use cache -async function CachedProfile() { - 'use cache' - const session = (await cookies()).get('session')?.value // Error! - return
{session}
-} - -// Correct - extract outside, pass as argument -async function ProfilePage() { - const session = (await cookies()).get('session')?.value - return -} - -async function CachedProfile({ sessionId }: { sessionId: string }) { - 'use cache' - // sessionId becomes part of cache key automatically - const data = await fetchUserData(sessionId) - return
{data.name}
-} -``` - -### Exception: `use cache: private` - -For compliance requirements when you can't refactor: - -```tsx -async function getData() { - 'use cache: private' - const session = (await cookies()).get('session')?.value // Allowed - return fetchData(session) -} -``` - ---- - -## Cache Key Generation - -Cache keys are automatic based on: -- **Build ID** - invalidates all caches on deploy -- **Function ID** - hash of function location -- **Serializable arguments** - props become part of key -- **Closure variables** - outer scope values included - -```tsx -async function Component({ userId }: { userId: string }) { - const getData = async (filter: string) => { - 'use cache' - // Cache key = userId (closure) + filter (argument) - return fetch(`/api/users/${userId}?filter=${filter}`) - } - return getData('active') -} -``` - ---- - -## Complete Example - -```tsx -import { Suspense } from 'react' -import { cookies } from 'next/headers' -import { cacheLife, cacheTag } from 'next/cache' - -export default function DashboardPage() { - return ( - <> - {/* Static shell - instant from CDN */} -

Dashboard

- - - {/* Cached - fast, revalidates hourly */} - - - {/* Dynamic - streams in with fresh data */} - }> - - - - ) -} - -async function Stats() { - 'use cache' - cacheLife('hours') - cacheTag('dashboard-stats') - - const stats = await db.stats.aggregate() - return -} - -async function Notifications() { - const userId = (await cookies()).get('userId')?.value - const notifications = await db.notifications.findMany({ - where: { userId, read: false } - }) - return -} -``` - ---- - -## Migration from Previous Versions - -| Old Config | Replacement | -|-----------|-------------| -| `experimental.ppr` | `cacheComponents: true` | -| `dynamic = 'force-dynamic'` | Remove (default behavior) | -| `dynamic = 'force-static'` | `'use cache'` + `cacheLife('max')` | -| `revalidate = N` | `cacheLife({ revalidate: N })` | -| `unstable_cache()` | `'use cache'` directive | - -### Migrating `unstable_cache` to `use cache` - -`unstable_cache` has been replaced by the `use cache` directive in Next.js 16. When `cacheComponents` is enabled, convert `unstable_cache` calls to `use cache` functions: - -**Before (`unstable_cache`):** - -```tsx -import { unstable_cache } from 'next/cache' - -const getCachedUser = unstable_cache( - async (id) => getUser(id), - ['my-app-user'], - { - tags: ['users'], - revalidate: 60, - } -) - -export default async function Page({ params }: { params: Promise<{ id: string }> }) { - const { id } = await params - const user = await getCachedUser(id) - return
{user.name}
-} -``` - -**After (`use cache`):** - -```tsx -import { cacheLife, cacheTag } from 'next/cache' - -async function getCachedUser(id: string) { - 'use cache' - cacheTag('users') - cacheLife({ revalidate: 60 }) - return getUser(id) -} - -export default async function Page({ params }: { params: Promise<{ id: string }> }) { - const { id } = await params - const user = await getCachedUser(id) - return
{user.name}
-} -``` - -Key differences: -- **No manual cache keys** - `use cache` generates keys automatically from function arguments and closures. The `keyParts` array from `unstable_cache` is no longer needed. -- **Tags** - Replace `options.tags` with `cacheTag()` calls inside the function. -- **Revalidation** - Replace `options.revalidate` with `cacheLife({ revalidate: N })` or a built-in profile like `cacheLife('minutes')`. -- **Dynamic data** - `unstable_cache` did not support `cookies()` or `headers()` inside the callback. The same restriction applies to `use cache`, but you can use `'use cache: private'` if needed. - ---- - -## Limitations - -- **Edge runtime not supported** - requires Node.js -- **Static export not supported** - needs server -- **Non-deterministic values** (`Math.random()`, `Date.now()`) execute once at build time inside `use cache` - -For request-time randomness outside cache: - -```tsx -import { connection } from 'next/server' - -async function DynamicContent() { - await connection() // Defer to request time - const id = crypto.randomUUID() // Different per request - return
{id}
-} -``` - -Sources: -- [Cache Components Guide](https://nextjs.org/docs/app/getting-started/cache-components) -- [use cache Directive](https://nextjs.org/docs/app/api-reference/directives/use-cache) -- [unstable_cache (legacy)](https://nextjs.org/docs/app/api-reference/functions/unstable_cache) diff --git a/skills/next-cache-components/overlay.yaml b/skills/next-cache-components/overlay.yaml deleted file mode 100644 index 1addecb..0000000 --- a/skills/next-cache-components/overlay.yaml +++ /dev/null @@ -1,78 +0,0 @@ -name: next-cache-components -description: Next.js 16 Cache Components guidance — PPR, use cache directive, cacheLife, cacheTag, updateTag, and migration from unstable_cache. Use when implementing partial prerendering, caching strategies, or migrating from older Next.js cache patterns. -metadata: - priority: 6 - docs: - - "https://nextjs.org/docs/app/getting-started/cache-components" - - "https://nextjs.org/docs/app/api-reference/directives/use-cache" - pathPatterns: - - 'next.config.*' - - 'app/**' - - 'src/app/**' - - 'apps/*/app/**' - - 'apps/*/src/app/**' - importPatterns: - - "next/cache" - bashPatterns: - - '\bnext\s+(dev|build)\b' - promptSignals: - phrases: - - "use cache" - - "cache components" - - "partial prerendering" - - "PPR" - - "cacheLife" - - "cacheTag" - - "updateTag" - - "unstable_cache" - allOf: - - [cache, component] - - [cache, directive] - - [partial, prerender] - anyOf: - - "revalidateTag" - - "stale" - - "revalidate" - - "cache profile" - noneOf: [] - minScore: 6 - validate: - - - pattern: 'unstable_cache\s*\(' - message: 'unstable_cache is deprecated in Next.js 16 — use the "use cache" directive with cacheTag() and cacheLife() instead' - severity: recommended - upgradeToSkill: next-cache-components - upgradeWhy: 'Guides migration from unstable_cache to use cache directive with cacheTag and cacheLife.' - - - pattern: '\bcacheHandler\s*:' - message: 'Singular cacheHandler is deprecated in Next.js 16 — use cacheHandlers (plural) with per-type handlers' - severity: recommended - - - pattern: revalidateTag\(\s*['"][^'"]+['"]\s*\) - message: 'Single-arg revalidateTag(tag) is deprecated in Next.js 16 — pass a cacheLife profile: revalidateTag(tag, "max")' - severity: recommended -retrieval: - aliases: - - cache components - - partial prerendering - - PPR - - use cache - intents: - - enable partial prerendering in Next.js - - cache async data with use cache directive - - invalidate cache with cacheTag - - migrate from unstable_cache - entities: - - use cache - - cacheLife - - cacheTag - - updateTag - - revalidateTag - - PPR -chainTo: - - - pattern: 'use cache' - targetSkill: nextjs - message: 'Cache component detected — loading Next.js best practices for RSC boundaries and data patterns alongside caching.' - skipIfFileContains: 'next-best-practices' - diff --git a/skills/next-cache-components/upstream/SKILL.md b/skills/next-cache-components/upstream/SKILL.md deleted file mode 100644 index 13a8961..0000000 --- a/skills/next-cache-components/upstream/SKILL.md +++ /dev/null @@ -1,411 +0,0 @@ ---- -name: next-cache-components -description: Next.js 16 Cache Components - PPR, use cache directive, cacheLife, cacheTag, updateTag ---- - -# Cache Components (Next.js 16+) - -Cache Components enable Partial Prerendering (PPR) - mix static, cached, and dynamic content in a single route. - -## Enable Cache Components - -```ts -// next.config.ts -import type { NextConfig } from 'next' - -const nextConfig: NextConfig = { - cacheComponents: true, -} - -export default nextConfig -``` - -This replaces the old `experimental.ppr` flag. - ---- - -## Three Content Types - -With Cache Components enabled, content falls into three categories: - -### 1. Static (Auto-Prerendered) - -Synchronous code, imports, pure computations - prerendered at build time: - -```tsx -export default function Page() { - return ( -
-

Our Blog

{/* Static - instant */} - -
- ) -} -``` - -### 2. Cached (`use cache`) - -Async data that doesn't need fresh fetches every request: - -```tsx -async function BlogPosts() { - 'use cache' - cacheLife('hours') - - const posts = await db.posts.findMany() - return -} -``` - -### 3. Dynamic (Suspense) - -Runtime data that must be fresh - wrap in Suspense: - -```tsx -import { Suspense } from 'react' - -export default function Page() { - return ( - <> - {/* Cached */} - - Loading...

}> - {/* Dynamic - streams in */} -
- - ) -} - -async function UserPreferences() { - const theme = (await cookies()).get('theme')?.value - return

Theme: {theme}

-} -``` - ---- - -## `use cache` Directive - -### File Level - -```tsx -'use cache' - -export default async function Page() { - // Entire page is cached - const data = await fetchData() - return
{data}
-} -``` - -### Component Level - -```tsx -export async function CachedComponent() { - 'use cache' - const data = await fetchData() - return
{data}
-} -``` - -### Function Level - -```tsx -export async function getData() { - 'use cache' - return db.query('SELECT * FROM posts') -} -``` - ---- - -## Cache Profiles - -### Built-in Profiles - -```tsx -'use cache' // Default: 5m stale, 15m revalidate -``` - -```tsx -'use cache: remote' // Platform-provided cache (Redis, KV) -``` - -```tsx -'use cache: private' // For compliance, allows runtime APIs -``` - -### `cacheLife()` - Custom Lifetime - -```tsx -import { cacheLife } from 'next/cache' - -async function getData() { - 'use cache' - cacheLife('hours') // Built-in profile - return fetch('/api/data') -} -``` - -Built-in profiles: `'default'`, `'minutes'`, `'hours'`, `'days'`, `'weeks'`, `'max'` - -### Inline Configuration - -```tsx -async function getData() { - 'use cache' - cacheLife({ - stale: 3600, // 1 hour - serve stale while revalidating - revalidate: 7200, // 2 hours - background revalidation interval - expire: 86400, // 1 day - hard expiration - }) - return fetch('/api/data') -} -``` - ---- - -## Cache Invalidation - -### `cacheTag()` - Tag Cached Content - -```tsx -import { cacheTag } from 'next/cache' - -async function getProducts() { - 'use cache' - cacheTag('products') - return db.products.findMany() -} - -async function getProduct(id: string) { - 'use cache' - cacheTag('products', `product-${id}`) - return db.products.findUnique({ where: { id } }) -} -``` - -### `updateTag()` - Immediate Invalidation - -Use when you need the cache refreshed within the same request: - -```tsx -'use server' - -import { updateTag } from 'next/cache' - -export async function updateProduct(id: string, data: FormData) { - await db.products.update({ where: { id }, data }) - updateTag(`product-${id}`) // Immediate - same request sees fresh data -} -``` - -### `revalidateTag()` - Background Revalidation - -Use for stale-while-revalidate behavior: - -```tsx -'use server' - -import { revalidateTag } from 'next/cache' - -export async function createPost(data: FormData) { - await db.posts.create({ data }) - revalidateTag('posts') // Background - next request sees fresh data -} -``` - ---- - -## Runtime Data Constraint - -**Cannot** access `cookies()`, `headers()`, or `searchParams` inside `use cache`. - -### Solution: Pass as Arguments - -```tsx -// Wrong - runtime API inside use cache -async function CachedProfile() { - 'use cache' - const session = (await cookies()).get('session')?.value // Error! - return
{session}
-} - -// Correct - extract outside, pass as argument -async function ProfilePage() { - const session = (await cookies()).get('session')?.value - return -} - -async function CachedProfile({ sessionId }: { sessionId: string }) { - 'use cache' - // sessionId becomes part of cache key automatically - const data = await fetchUserData(sessionId) - return
{data.name}
-} -``` - -### Exception: `use cache: private` - -For compliance requirements when you can't refactor: - -```tsx -async function getData() { - 'use cache: private' - const session = (await cookies()).get('session')?.value // Allowed - return fetchData(session) -} -``` - ---- - -## Cache Key Generation - -Cache keys are automatic based on: -- **Build ID** - invalidates all caches on deploy -- **Function ID** - hash of function location -- **Serializable arguments** - props become part of key -- **Closure variables** - outer scope values included - -```tsx -async function Component({ userId }: { userId: string }) { - const getData = async (filter: string) => { - 'use cache' - // Cache key = userId (closure) + filter (argument) - return fetch(`/api/users/${userId}?filter=${filter}`) - } - return getData('active') -} -``` - ---- - -## Complete Example - -```tsx -import { Suspense } from 'react' -import { cookies } from 'next/headers' -import { cacheLife, cacheTag } from 'next/cache' - -export default function DashboardPage() { - return ( - <> - {/* Static shell - instant from CDN */} -

Dashboard

- - - {/* Cached - fast, revalidates hourly */} - - - {/* Dynamic - streams in with fresh data */} - }> - - - - ) -} - -async function Stats() { - 'use cache' - cacheLife('hours') - cacheTag('dashboard-stats') - - const stats = await db.stats.aggregate() - return -} - -async function Notifications() { - const userId = (await cookies()).get('userId')?.value - const notifications = await db.notifications.findMany({ - where: { userId, read: false } - }) - return -} -``` - ---- - -## Migration from Previous Versions - -| Old Config | Replacement | -|-----------|-------------| -| `experimental.ppr` | `cacheComponents: true` | -| `dynamic = 'force-dynamic'` | Remove (default behavior) | -| `dynamic = 'force-static'` | `'use cache'` + `cacheLife('max')` | -| `revalidate = N` | `cacheLife({ revalidate: N })` | -| `unstable_cache()` | `'use cache'` directive | - -### Migrating `unstable_cache` to `use cache` - -`unstable_cache` has been replaced by the `use cache` directive in Next.js 16. When `cacheComponents` is enabled, convert `unstable_cache` calls to `use cache` functions: - -**Before (`unstable_cache`):** - -```tsx -import { unstable_cache } from 'next/cache' - -const getCachedUser = unstable_cache( - async (id) => getUser(id), - ['my-app-user'], - { - tags: ['users'], - revalidate: 60, - } -) - -export default async function Page({ params }: { params: Promise<{ id: string }> }) { - const { id } = await params - const user = await getCachedUser(id) - return
{user.name}
-} -``` - -**After (`use cache`):** - -```tsx -import { cacheLife, cacheTag } from 'next/cache' - -async function getCachedUser(id: string) { - 'use cache' - cacheTag('users') - cacheLife({ revalidate: 60 }) - return getUser(id) -} - -export default async function Page({ params }: { params: Promise<{ id: string }> }) { - const { id } = await params - const user = await getCachedUser(id) - return
{user.name}
-} -``` - -Key differences: -- **No manual cache keys** - `use cache` generates keys automatically from function arguments and closures. The `keyParts` array from `unstable_cache` is no longer needed. -- **Tags** - Replace `options.tags` with `cacheTag()` calls inside the function. -- **Revalidation** - Replace `options.revalidate` with `cacheLife({ revalidate: N })` or a built-in profile like `cacheLife('minutes')`. -- **Dynamic data** - `unstable_cache` did not support `cookies()` or `headers()` inside the callback. The same restriction applies to `use cache`, but you can use `'use cache: private'` if needed. - ---- - -## Limitations - -- **Edge runtime not supported** - requires Node.js -- **Static export not supported** - needs server -- **Non-deterministic values** (`Math.random()`, `Date.now()`) execute once at build time inside `use cache` - -For request-time randomness outside cache: - -```tsx -import { connection } from 'next/server' - -async function DynamicContent() { - await connection() // Defer to request time - const id = crypto.randomUUID() // Different per request - return
{id}
-} -``` - -Sources: -- [Cache Components Guide](https://nextjs.org/docs/app/getting-started/cache-components) -- [use cache Directive](https://nextjs.org/docs/app/api-reference/directives/use-cache) -- [unstable_cache (legacy)](https://nextjs.org/docs/app/api-reference/functions/unstable_cache) diff --git a/skills/next-upgrade/SKILL.md b/skills/next-upgrade/SKILL.md deleted file mode 100644 index e2ca3ff..0000000 --- a/skills/next-upgrade/SKILL.md +++ /dev/null @@ -1,103 +0,0 @@ ---- -name: next-upgrade -description: Upgrade Next.js to the latest version following official migration guides and codemods. Use when upgrading Next.js versions, running codemods, or migrating between major releases. -metadata: - priority: 6 - docs: - - "https://nextjs.org/docs/app/guides/upgrading" - - "https://nextjs.org/docs/app/guides/upgrading/codemods" - pathPatterns: - - 'next.config.*' - - 'package.json' - bashPatterns: - - '\bnpx\s+@next/codemod\b' - - '\bnpm\s+(install|i|add)\s+[^\n]*\bnext@' - - '\bpnpm\s+(install|i|add)\s+[^\n]*\bnext@' - - '\bbun\s+(install|i|add)\s+[^\n]*\bnext@' - - '\byarn\s+add\s+[^\n]*\bnext@' - promptSignals: - phrases: - - "upgrade next" - - "upgrade nextjs" - - "migrate next" - - "update next.js" - - "next.js upgrade" - - "nextjs migration" - - "next codemod" - allOf: - - [upgrade, next] - - [migrate, next] - - [update, nextjs] - anyOf: - - "breaking changes" - - "codemod" - - "migration guide" - - "version upgrade" - noneOf: [] - minScore: 6 -retrieval: - aliases: - - next upgrade - - nextjs migration - - next codemod - intents: - - upgrade Next.js to latest version - - run Next.js codemods - - migrate between major Next.js versions - entities: - - codemod - - migration - - upgrade - - breaking changes -chainTo: - - - pattern: 'getServerSideProps|getStaticProps|next/router|next/head|next/document' - targetSkill: nextjs - message: 'Pages Router patterns detected during upgrade — loading Next.js best practices for App Router migration.' - ---- - -# Upgrade Next.js - -Upgrade the current project to the latest Next.js version following official migration guides. - -## Instructions - -1. **Detect current version**: Read `package.json` to identify the current Next.js version and related dependencies (React, React DOM, etc.) - -2. **Fetch the latest upgrade guide**: Use WebFetch to get the official upgrade documentation: - - Codemods: https://nextjs.org/docs/app/guides/upgrading/codemods - - Version-specific guides (adjust version as needed): - - https://nextjs.org/docs/app/guides/upgrading/version-16 - - https://nextjs.org/docs/app/guides/upgrading/version-15 - - https://nextjs.org/docs/app/guides/upgrading/version-14 - -3. **Determine upgrade path**: Based on current version, identify which migration steps apply. For major version jumps, upgrade incrementally (e.g., 13 → 14 → 15). - -4. **Run codemods first**: Next.js provides codemods to automate breaking changes: - ```bash - npx @next/codemod@latest - ``` - Common transforms: - - `next-async-request-api` - Updates async Request APIs (v15) - - `next-request-geo-ip` - Migrates geo/ip properties (v15) - - `next-dynamic-access-named-export` - Transforms dynamic imports (v15) - -5. **Update dependencies**: Upgrade Next.js and peer dependencies together: - ```bash - npm install next@latest react@latest react-dom@latest - ``` - -6. **Review breaking changes**: Check the upgrade guide for manual changes needed: - - API changes (e.g., async params in v15) - - Configuration changes in `next.config.js` - - Deprecated features being removed - -7. **Update TypeScript types** (if applicable): - ```bash - npm install @types/react@latest @types/react-dom@latest - ``` - -8. **Test the upgrade**: - - Run `npm run build` to check for build errors - - Run `npm run dev` and test key functionality diff --git a/skills/next-upgrade/overlay.yaml b/skills/next-upgrade/overlay.yaml deleted file mode 100644 index a3d729e..0000000 --- a/skills/next-upgrade/overlay.yaml +++ /dev/null @@ -1,56 +0,0 @@ -name: next-upgrade -description: Upgrade Next.js to the latest version following official migration guides and codemods. Use when upgrading Next.js versions, running codemods, or migrating between major releases. -metadata: - priority: 6 - docs: - - "https://nextjs.org/docs/app/guides/upgrading" - - "https://nextjs.org/docs/app/guides/upgrading/codemods" - pathPatterns: - - 'next.config.*' - - 'package.json' - bashPatterns: - - '\bnpx\s+@next/codemod\b' - - '\bnpm\s+(install|i|add)\s+[^\n]*\bnext@' - - '\bpnpm\s+(install|i|add)\s+[^\n]*\bnext@' - - '\bbun\s+(install|i|add)\s+[^\n]*\bnext@' - - '\byarn\s+add\s+[^\n]*\bnext@' - promptSignals: - phrases: - - "upgrade next" - - "upgrade nextjs" - - "migrate next" - - "update next.js" - - "next.js upgrade" - - "nextjs migration" - - "next codemod" - allOf: - - [upgrade, next] - - [migrate, next] - - [update, nextjs] - anyOf: - - "breaking changes" - - "codemod" - - "migration guide" - - "version upgrade" - noneOf: [] - minScore: 6 -retrieval: - aliases: - - next upgrade - - nextjs migration - - next codemod - intents: - - upgrade Next.js to latest version - - run Next.js codemods - - migrate between major Next.js versions - entities: - - codemod - - migration - - upgrade - - breaking changes -chainTo: - - - pattern: 'getServerSideProps|getStaticProps|next/router|next/head|next/document' - targetSkill: nextjs - message: 'Pages Router patterns detected during upgrade — loading Next.js best practices for App Router migration.' - diff --git a/skills/next-upgrade/upstream/SKILL.md b/skills/next-upgrade/upstream/SKILL.md deleted file mode 100644 index a96bf91..0000000 --- a/skills/next-upgrade/upstream/SKILL.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -name: next-upgrade -description: Upgrade Next.js to the latest version following official migration guides and codemods -argument-hint: "[target-version]" ---- - -# Upgrade Next.js - -Upgrade the current project to the latest Next.js version following official migration guides. - -## Instructions - -1. **Detect current version**: Read `package.json` to identify the current Next.js version and related dependencies (React, React DOM, etc.) - -2. **Fetch the latest upgrade guide**: Use WebFetch to get the official upgrade documentation: - - Codemods: https://nextjs.org/docs/app/guides/upgrading/codemods - - Version-specific guides (adjust version as needed): - - https://nextjs.org/docs/app/guides/upgrading/version-16 - - https://nextjs.org/docs/app/guides/upgrading/version-15 - - https://nextjs.org/docs/app/guides/upgrading/version-14 - -3. **Determine upgrade path**: Based on current version, identify which migration steps apply. For major version jumps, upgrade incrementally (e.g., 13 → 14 → 15). - -4. **Run codemods first**: Next.js provides codemods to automate breaking changes: - ```bash - npx @next/codemod@latest - ``` - Common transforms: - - `next-async-request-api` - Updates async Request APIs (v15) - - `next-request-geo-ip` - Migrates geo/ip properties (v15) - - `next-dynamic-access-named-export` - Transforms dynamic imports (v15) - -5. **Update dependencies**: Upgrade Next.js and peer dependencies together: - ```bash - npm install next@latest react@latest react-dom@latest - ``` - -6. **Review breaking changes**: Check the upgrade guide for manual changes needed: - - API changes (e.g., async params in v15) - - Configuration changes in `next.config.js` - - Deprecated features being removed - -7. **Update TypeScript types** (if applicable): - ```bash - npm install @types/react@latest @types/react-dom@latest - ``` - -8. **Test the upgrade**: - - Run `npm run build` to check for build errors - - Run `npm run dev` and test key functionality diff --git a/skills/nextjs/SKILL.md b/skills/nextjs/SKILL.md deleted file mode 100644 index 07fae30..0000000 --- a/skills/nextjs/SKILL.md +++ /dev/null @@ -1,434 +0,0 @@ ---- -name: nextjs -description: Next.js App Router expert guidance. Use when building, debugging, or architecting Next.js applications — routing, Server Components, Server Actions, Cache Components, layouts, middleware/proxy, data fetching, rendering strategies, and deployment on Vercel. -metadata: - priority: 5 - docs: - - "https://nextjs.org/docs" - - "https://nextjs.org/docs/app" - sitemap: "https://nextjs.org/sitemap.xml" - pathPatterns: - - 'next.config.*' - - 'next-env.d.ts' - - 'app/**' - - 'pages/**' - - 'src/app/**' - - 'src/pages/**' - - 'tailwind.config.*' - - 'postcss.config.*' - - 'tsconfig.json' - - 'tsconfig.*.json' - - 'apps/*/app/**' - - 'apps/*/pages/**' - - 'apps/*/src/app/**' - - 'apps/*/src/pages/**' - - 'apps/*/next.config.*' - bashPatterns: - - '\bnext\s+(dev|build|start|lint)\b' - - '\bnext\s+experimental-analyze\b' - - '\bnpx\s+create-next-app\b' - - '\bbunx\s+create-next-app\b' - - '\bnpm\s+run\s+(dev|build|start)\b' - - '\bpnpm\s+(dev|build)\b' - - '\bbun\s+run\s+(dev|build)\b' - promptSignals: - phrases: - - "next.js" - - "nextjs" - - "app router" - - "server component" - - "server action" - allOf: - - [middleware, next] - - [layout, route] - anyOf: - - "pages router" - - "getserversideprops" - - "use server" - noneOf: [] - minScore: 6 -validate: - - - pattern: export.*getServerSideProps - message: 'getServerSideProps is removed in App Router — use server components or route handlers' - severity: error - upgradeToSkill: nextjs - upgradeWhy: 'Guides migration from Pages Router getServerSideProps to App Router server components with async data fetching.' - - - pattern: getServerSideProps - message: 'getServerSideProps is a Pages Router pattern — migrate to App Router server components' - severity: warn - - - pattern: export.*getStaticProps - message: 'getStaticProps is removed in App Router — use generateStaticParams + server components instead' - severity: error - upgradeToSkill: nextjs - upgradeWhy: 'Guides migration from Pages Router getStaticProps to App Router generateStaticParams with server components.' - - - pattern: getStaticProps - message: 'getStaticProps is a Pages Router pattern — migrate to App Router generateStaticParams + server components' - severity: warn - - - pattern: from\s+['"]next/router['"] - message: 'next/router is Pages Router only — use next/navigation for App Router' - severity: error - upgradeToSkill: nextjs - upgradeWhy: 'Guides migration from next/router to next/navigation with useRouter, usePathname, useSearchParams hooks.' - - - pattern: (useState|useEffect) - message: 'React hooks require "use client" directive — add it at the top of client components' - severity: warn - skipIfFileContains: "^['\"]use client['\"]" - - - pattern: from\s+['"]next/head['"] - message: 'next/head is Pages Router — use export const metadata or generateMetadata() in App Router. Run Skill(nextjs) for metadata API guidance.' - severity: error - upgradeToSkill: nextjs - upgradeWhy: 'Guides migration from next/head to the App Router metadata API (export const metadata / generateMetadata()).' - skipIfFileContains: export\s+(const\s+)?metadata|generateMetadata - - - pattern: export\s+(default\s+)?function\s+middleware - message: 'middleware() is renamed to proxy() in Next.js 16 — rename the function and the file to proxy.ts. Run Skill(routing-middleware) for proxy.ts migration guidance.' - severity: recommended - upgradeToSkill: routing-middleware - upgradeWhy: 'Guides migration from middleware.ts to proxy.ts with correct file placement, runtime config, and request interception patterns.' - - - pattern: revalidateTag\(\s*['"][^'"]+['"]\s*\) - message: 'Single-arg revalidateTag(tag) is deprecated in Next.js 16 — pass a cacheLife profile: revalidateTag(tag, "max")' - severity: recommended - upgradeToSkill: nextjs - upgradeWhy: 'Guides migration from single-arg revalidateTag to the Next.js 16 two-arg API with cacheLife profiles.' - - - pattern: '\bcacheHandler\s*:' - message: 'Singular cacheHandler is deprecated in Next.js 16 — use cacheHandlers (plural) with per-type handlers' - severity: recommended - upgradeToSkill: nextjs - upgradeWhy: 'Guides migration from singular cacheHandler to Next.js 16 cacheHandlers (plural) with per-type handler config.' - - - pattern: useRef\(\s*\) - message: 'useRef() requires an initial value in React 19 — use useRef(null) or useRef(0)' - severity: error - - - pattern: next\s+export - message: 'next export was removed — use output: "export" in next.config.js for static export' - severity: error - upgradeToSkill: nextjs - upgradeWhy: 'Guides migration from next export CLI command to output: "export" in next.config for static site generation.' - - - pattern: (?]*fonts\.googleapis' - message: 'External font loader detected. Use next/font for zero-CLS, self-hosted font loading with automatic optimization.' - severity: recommended - upgradeToSkill: nextjs - upgradeWhy: 'Guides migration from external font loaders to next/font with Geist Sans/Mono for zero-CLS font optimization.' - skipIfFileContains: 'next/font' -chainTo: - - - pattern: 'export\s+(default\s+)?function\s+middleware' - targetSkill: routing-middleware - message: 'middleware() is renamed to proxy() in Next.js 16 — loading Routing Middleware guidance for proxy.ts migration.' - - - pattern: "from\\s+['\"]@vercel/(postgres|kv)['\"]" - targetSkill: vercel-storage - message: 'Sunset storage package detected — loading Vercel Storage guidance for Neon/Upstash migration.' - - - pattern: "from\\s+['\"]@ai-sdk/(anthropic|openai)['\"]" - targetSkill: ai-gateway - message: 'Direct AI provider SDK import — loading AI Gateway guidance for unified model routing with failover and cost tracking.' - - - pattern: 'from\s+[''""](next-auth|@auth/core)[''""]|NextAuth\(|getServerSession\(' - targetSkill: auth - message: 'Legacy auth pattern detected — loading managed authentication guidance (Clerk, Descope, Auth0).' - - - pattern: 'NextApiRequest|NextApiResponse|export\s+default\s+function\s+handler' - targetSkill: vercel-functions - message: 'Pages Router API handler detected — loading Vercel Functions guidance for App Router migration.' - - - pattern: 'from\s+[''""](lru-cache|node-cache|memory-cache)[''""]|new\s+(LRUCache|NodeCache)\(' - targetSkill: runtime-cache - message: 'In-process cache detected — loading Runtime Cache guidance for serverless-compatible caching.' - - - pattern: 'fetch\s*\(\s*[''""](https?://)?(api\.openai\.com|api\.anthropic\.com|api\.cohere\.ai)' - targetSkill: ai-gateway - message: 'Raw AI provider fetch URL detected — loading AI Gateway guidance for unified routing, failover, and OIDC auth.' - skipIfFileContains: '@ai-sdk/|from\s+[''""](ai)[''""]|ai-gateway|gateway\(' - - - pattern: 'jwt\.(sign|verify|decode)\(|from\s+[''""](jsonwebtoken)[''""]|new\s+SignJWT\(|jwtVerify\(' - targetSkill: auth - message: 'Manual JWT token handling detected — loading Auth guidance for managed authentication (Clerk, Descope, Auth0).' - skipIfFileContains: 'clerkMiddleware|@clerk/|@auth0/|@descope/|from\s+[''""](next-auth)[''""]' - - - pattern: 'from\s+[''"]@/components/ui/|from\s+[''"]@/components/ui[''""]' - targetSkill: shadcn - message: 'shadcn/ui component imports detected — loading shadcn guidance for component composition, theming, and registry patterns.' - skipIfFileContains: 'shadcn|components\.json' - - - pattern: 'from\s+[''""](styled-components|@emotion/(react|styled)|@mui/material)[''""]' - targetSkill: shadcn - message: 'CSS-in-JS library detected — loading shadcn/ui guidance for Tailwind CSS + Radix UI component patterns (Vercel recommended).' - skipIfFileContains: '@/components/ui|shadcn' - - - pattern: 'getInitialProps' - targetSkill: nextjs - message: 'getInitialProps is a legacy Pages Router pattern — loading Next.js guidance for App Router migration with server components and async data fetching.' - skipIfFileContains: 'app/.*page\.|generateStaticParams|use cache' - - - pattern: 'export.*getServerSideProps|getServerSideProps\s*\(' - targetSkill: nextjs - message: 'getServerSideProps is a Pages Router pattern — loading Next.js guidance for App Router migration with server components and async data fetching.' - skipIfFileContains: 'generateStaticParams|use cache|app/.*page\.' - - - pattern: 'export.*getStaticProps|getStaticProps\s*\(' - targetSkill: nextjs - message: 'getStaticProps is a Pages Router pattern — loading Next.js guidance for App Router migration with generateStaticParams and server components.' - skipIfFileContains: 'generateStaticParams|use cache|app/.*page\.' - - - pattern: '_app\.(tsx?|jsx?)' - targetSkill: nextjs - message: '_app.tsx is a Pages Router pattern — loading Next.js guidance for App Router layout.tsx migration.' - skipIfFileContains: 'app/layout\.|app/.*layout\.' - - - pattern: '_document\.(tsx?|jsx?)' - targetSkill: nextjs - message: '_document.tsx is a Pages Router pattern — loading Next.js guidance for App Router layout.tsx with metadata API migration.' - skipIfFileContains: 'app/layout\.|app/.*layout\.' - - - pattern: "from\\s+['\"]next/document['\"]" - targetSkill: nextjs - message: 'next/document is Pages Router only — loading Next.js guidance for App Router layout.tsx with html/body structure.' - skipIfFileContains: 'app/layout\.|app/.*layout\.' - - - pattern: 'pages/api/' - targetSkill: vercel-functions - message: 'Pages Router API route (pages/api/) detected — loading Vercel Functions guidance for App Router route handlers with named HTTP exports.' - skipIfFileContains: 'export\s+(async\s+)?function\s+(GET|POST|PUT|PATCH|DELETE)' -retrieval: - aliases: - - next.js - - nextjs app - - react framework - - app router - intents: - - set up routing and layouts in a Next.js app - - choose between server and client components for a feature - - configure data fetching or caching in App Router - - add middleware or proxy logic to handle requests - - set up server rendering for React pages - - add a new page with dynamic route segments - entities: - - App Router - - Server Components - - Server Actions - - generateMetadata - - layout - - proxy - - next.config - examples: - - add a new page with dynamic routing - - should this be a server or client component - - set up middleware for auth redirects - - configure caching for this data fetch - - set up server rendering for my pages - ---- - -# Next.js Best Practices - -Apply these rules when writing or reviewing Next.js code. - -## File Conventions - -See [file-conventions.md](./file-conventions.md) for: -- Project structure and special files -- Route segments (dynamic, catch-all, groups) -- Parallel and intercepting routes -- Middleware rename in v16 (middleware → proxy) - -## RSC Boundaries - -Detect invalid React Server Component patterns. - -See [rsc-boundaries.md](./rsc-boundaries.md) for: -- Async client component detection (invalid) -- Non-serializable props detection -- Server Action exceptions - -## Async Patterns - -Next.js 15+ async API changes. - -See [async-patterns.md](./async-patterns.md) for: -- Async `params` and `searchParams` -- Async `cookies()` and `headers()` -- Migration codemod - -## Runtime Selection - -See [runtime-selection.md](./runtime-selection.md) for: -- Default to Node.js runtime -- When Edge runtime is appropriate - -## Directives - -See [directives.md](./directives.md) for: -- `'use client'`, `'use server'` (React) -- `'use cache'` (Next.js) - -## Functions - -See [functions.md](./functions.md) for: -- Navigation hooks: `useRouter`, `usePathname`, `useSearchParams`, `useParams` -- Server functions: `cookies`, `headers`, `draftMode`, `after` -- Generate functions: `generateStaticParams`, `generateMetadata` - -## Error Handling - -See [error-handling.md](./error-handling.md) for: -- `error.tsx`, `global-error.tsx`, `not-found.tsx` -- `redirect`, `permanentRedirect`, `notFound` -- `forbidden`, `unauthorized` (auth errors) -- `unstable_rethrow` for catch blocks - -## Data Patterns - -See [data-patterns.md](./data-patterns.md) for: -- Server Components vs Server Actions vs Route Handlers -- Avoiding data waterfalls (`Promise.all`, Suspense, preload) -- Client component data fetching - -## Route Handlers - -See [route-handlers.md](./route-handlers.md) for: -- `route.ts` basics -- GET handler conflicts with `page.tsx` -- Environment behavior (no React DOM) -- When to use vs Server Actions - -## Metadata & OG Images - -See [metadata.md](./metadata.md) for: -- Static and dynamic metadata -- `generateMetadata` function -- OG image generation with `next/og` -- File-based metadata conventions - -## Image Optimization - -See [image.md](./image.md) for: -- Always use `next/image` over `` -- Remote images configuration -- Responsive `sizes` attribute -- Blur placeholders -- Priority loading for LCP - -## Font Optimization - -See [font.md](./font.md) for: -- `next/font` setup -- Google Fonts, local fonts -- Tailwind CSS integration -- Preloading subsets - -## Bundling - -See [bundling.md](./bundling.md) for: -- Server-incompatible packages -- CSS imports (not link tags) -- Polyfills (already included) -- ESM/CommonJS issues -- Bundle analysis - -## Scripts - -See [scripts.md](./scripts.md) for: -- `next/script` vs native script tags -- Inline scripts need `id` -- Loading strategies -- Google Analytics with `@next/third-parties` - -## Hydration Errors - -See [hydration-error.md](./hydration-error.md) for: -- Common causes (browser APIs, dates, invalid HTML) -- Debugging with error overlay -- Fixes for each cause - -## Suspense Boundaries - -See [suspense-boundaries.md](./suspense-boundaries.md) for: -- CSR bailout with `useSearchParams` and `usePathname` -- Which hooks require Suspense boundaries - -## Parallel & Intercepting Routes - -See [parallel-routes.md](./parallel-routes.md) for: -- Modal patterns with `@slot` and `(.)` interceptors -- `default.tsx` for fallbacks -- Closing modals correctly with `router.back()` - -## Self-Hosting - -See [self-hosting.md](./self-hosting.md) for: -- `output: 'standalone'` for Docker -- Cache handlers for multi-instance ISR -- What works vs needs extra setup - -## Debug Tricks - -See [debug-tricks.md](./debug-tricks.md) for: -- MCP endpoint for AI-assisted debugging -- Rebuild specific routes with `--debug-build-paths` - diff --git a/skills/nextjs/overlay.yaml b/skills/nextjs/overlay.yaml deleted file mode 100644 index a049e11..0000000 --- a/skills/nextjs/overlay.yaml +++ /dev/null @@ -1,284 +0,0 @@ -name: nextjs -description: Next.js App Router expert guidance. Use when building, debugging, or architecting Next.js applications — routing, Server Components, Server Actions, Cache Components, layouts, middleware/proxy, data fetching, rendering strategies, and deployment on Vercel. -metadata: - priority: 5 - docs: - - "https://nextjs.org/docs" - - "https://nextjs.org/docs/app" - sitemap: "https://nextjs.org/sitemap.xml" - pathPatterns: - - 'next.config.*' - - 'next-env.d.ts' - - 'app/**' - - 'pages/**' - - 'src/app/**' - - 'src/pages/**' - - 'tailwind.config.*' - - 'postcss.config.*' - - 'tsconfig.json' - - 'tsconfig.*.json' - - 'apps/*/app/**' - - 'apps/*/pages/**' - - 'apps/*/src/app/**' - - 'apps/*/src/pages/**' - - 'apps/*/next.config.*' - bashPatterns: - - '\bnext\s+(dev|build|start|lint)\b' - - '\bnext\s+experimental-analyze\b' - - '\bnpx\s+create-next-app\b' - - '\bbunx\s+create-next-app\b' - - '\bnpm\s+run\s+(dev|build|start)\b' - - '\bpnpm\s+(dev|build)\b' - - '\bbun\s+run\s+(dev|build)\b' - promptSignals: - phrases: - - "next.js" - - "nextjs" - - "app router" - - "server component" - - "server action" - allOf: - - [middleware, next] - - [layout, route] - anyOf: - - "pages router" - - "getserversideprops" - - "use server" - noneOf: [] - minScore: 6 -validate: - - - pattern: export.*getServerSideProps - message: 'getServerSideProps is removed in App Router — use server components or route handlers' - severity: error - upgradeToSkill: nextjs - upgradeWhy: 'Guides migration from Pages Router getServerSideProps to App Router server components with async data fetching.' - - - pattern: getServerSideProps - message: 'getServerSideProps is a Pages Router pattern — migrate to App Router server components' - severity: warn - - - pattern: export.*getStaticProps - message: 'getStaticProps is removed in App Router — use generateStaticParams + server components instead' - severity: error - upgradeToSkill: nextjs - upgradeWhy: 'Guides migration from Pages Router getStaticProps to App Router generateStaticParams with server components.' - - - pattern: getStaticProps - message: 'getStaticProps is a Pages Router pattern — migrate to App Router generateStaticParams + server components' - severity: warn - - - pattern: from\s+['"]next/router['"] - message: 'next/router is Pages Router only — use next/navigation for App Router' - severity: error - upgradeToSkill: nextjs - upgradeWhy: 'Guides migration from next/router to next/navigation with useRouter, usePathname, useSearchParams hooks.' - - - pattern: (useState|useEffect) - message: 'React hooks require "use client" directive — add it at the top of client components' - severity: warn - skipIfFileContains: "^['\"]use client['\"]" - - - pattern: from\s+['"]next/head['"] - message: 'next/head is Pages Router — use export const metadata or generateMetadata() in App Router. Run Skill(nextjs) for metadata API guidance.' - severity: error - upgradeToSkill: nextjs - upgradeWhy: 'Guides migration from next/head to the App Router metadata API (export const metadata / generateMetadata()).' - skipIfFileContains: export\s+(const\s+)?metadata|generateMetadata - - - pattern: export\s+(default\s+)?function\s+middleware - message: 'middleware() is renamed to proxy() in Next.js 16 — rename the function and the file to proxy.ts. Run Skill(routing-middleware) for proxy.ts migration guidance.' - severity: recommended - upgradeToSkill: routing-middleware - upgradeWhy: 'Guides migration from middleware.ts to proxy.ts with correct file placement, runtime config, and request interception patterns.' - - - pattern: revalidateTag\(\s*['"][^'"]+['"]\s*\) - message: 'Single-arg revalidateTag(tag) is deprecated in Next.js 16 — pass a cacheLife profile: revalidateTag(tag, "max")' - severity: recommended - upgradeToSkill: nextjs - upgradeWhy: 'Guides migration from single-arg revalidateTag to the Next.js 16 two-arg API with cacheLife profiles.' - - - pattern: '\bcacheHandler\s*:' - message: 'Singular cacheHandler is deprecated in Next.js 16 — use cacheHandlers (plural) with per-type handlers' - severity: recommended - upgradeToSkill: nextjs - upgradeWhy: 'Guides migration from singular cacheHandler to Next.js 16 cacheHandlers (plural) with per-type handler config.' - - - pattern: useRef\(\s*\) - message: 'useRef() requires an initial value in React 19 — use useRef(null) or useRef(0)' - severity: error - - - pattern: next\s+export - message: 'next export was removed — use output: "export" in next.config.js for static export' - severity: error - upgradeToSkill: nextjs - upgradeWhy: 'Guides migration from next export CLI command to output: "export" in next.config for static site generation.' - - - pattern: (?]*fonts\.googleapis' - message: 'External font loader detected. Use next/font for zero-CLS, self-hosted font loading with automatic optimization.' - severity: recommended - upgradeToSkill: nextjs - upgradeWhy: 'Guides migration from external font loaders to next/font with Geist Sans/Mono for zero-CLS font optimization.' - skipIfFileContains: 'next/font' -chainTo: - - - pattern: 'export\s+(default\s+)?function\s+middleware' - targetSkill: routing-middleware - message: 'middleware() is renamed to proxy() in Next.js 16 — loading Routing Middleware guidance for proxy.ts migration.' - - - pattern: "from\\s+['\"]@vercel/(postgres|kv)['\"]" - targetSkill: vercel-storage - message: 'Sunset storage package detected — loading Vercel Storage guidance for Neon/Upstash migration.' - - - pattern: "from\\s+['\"]@ai-sdk/(anthropic|openai)['\"]" - targetSkill: ai-gateway - message: 'Direct AI provider SDK import — loading AI Gateway guidance for unified model routing with failover and cost tracking.' - - - pattern: 'from\s+[''""](next-auth|@auth/core)[''""]|NextAuth\(|getServerSession\(' - targetSkill: auth - message: 'Legacy auth pattern detected — loading managed authentication guidance (Clerk, Descope, Auth0).' - - - pattern: 'NextApiRequest|NextApiResponse|export\s+default\s+function\s+handler' - targetSkill: vercel-functions - message: 'Pages Router API handler detected — loading Vercel Functions guidance for App Router migration.' - - - pattern: 'from\s+[''""](lru-cache|node-cache|memory-cache)[''""]|new\s+(LRUCache|NodeCache)\(' - targetSkill: runtime-cache - message: 'In-process cache detected — loading Runtime Cache guidance for serverless-compatible caching.' - - - pattern: 'fetch\s*\(\s*[''""](https?://)?(api\.openai\.com|api\.anthropic\.com|api\.cohere\.ai)' - targetSkill: ai-gateway - message: 'Raw AI provider fetch URL detected — loading AI Gateway guidance for unified routing, failover, and OIDC auth.' - skipIfFileContains: '@ai-sdk/|from\s+[''""](ai)[''""]|ai-gateway|gateway\(' - - - pattern: 'jwt\.(sign|verify|decode)\(|from\s+[''""](jsonwebtoken)[''""]|new\s+SignJWT\(|jwtVerify\(' - targetSkill: auth - message: 'Manual JWT token handling detected — loading Auth guidance for managed authentication (Clerk, Descope, Auth0).' - skipIfFileContains: 'clerkMiddleware|@clerk/|@auth0/|@descope/|from\s+[''""](next-auth)[''""]' - - - pattern: 'from\s+[''"]@/components/ui/|from\s+[''"]@/components/ui[''""]' - targetSkill: shadcn - message: 'shadcn/ui component imports detected — loading shadcn guidance for component composition, theming, and registry patterns.' - skipIfFileContains: 'shadcn|components\.json' - - - pattern: 'from\s+[''""](styled-components|@emotion/(react|styled)|@mui/material)[''""]' - targetSkill: shadcn - message: 'CSS-in-JS library detected — loading shadcn/ui guidance for Tailwind CSS + Radix UI component patterns (Vercel recommended).' - skipIfFileContains: '@/components/ui|shadcn' - - - pattern: 'getInitialProps' - targetSkill: nextjs - message: 'getInitialProps is a legacy Pages Router pattern — loading Next.js guidance for App Router migration with server components and async data fetching.' - skipIfFileContains: 'app/.*page\.|generateStaticParams|use cache' - - - pattern: 'export.*getServerSideProps|getServerSideProps\s*\(' - targetSkill: nextjs - message: 'getServerSideProps is a Pages Router pattern — loading Next.js guidance for App Router migration with server components and async data fetching.' - skipIfFileContains: 'generateStaticParams|use cache|app/.*page\.' - - - pattern: 'export.*getStaticProps|getStaticProps\s*\(' - targetSkill: nextjs - message: 'getStaticProps is a Pages Router pattern — loading Next.js guidance for App Router migration with generateStaticParams and server components.' - skipIfFileContains: 'generateStaticParams|use cache|app/.*page\.' - - - pattern: '_app\.(tsx?|jsx?)' - targetSkill: nextjs - message: '_app.tsx is a Pages Router pattern — loading Next.js guidance for App Router layout.tsx migration.' - skipIfFileContains: 'app/layout\.|app/.*layout\.' - - - pattern: '_document\.(tsx?|jsx?)' - targetSkill: nextjs - message: '_document.tsx is a Pages Router pattern — loading Next.js guidance for App Router layout.tsx with metadata API migration.' - skipIfFileContains: 'app/layout\.|app/.*layout\.' - - - pattern: "from\\s+['\"]next/document['\"]" - targetSkill: nextjs - message: 'next/document is Pages Router only — loading Next.js guidance for App Router layout.tsx with html/body structure.' - skipIfFileContains: 'app/layout\.|app/.*layout\.' - - - pattern: 'pages/api/' - targetSkill: vercel-functions - message: 'Pages Router API route (pages/api/) detected — loading Vercel Functions guidance for App Router route handlers with named HTTP exports.' - skipIfFileContains: 'export\s+(async\s+)?function\s+(GET|POST|PUT|PATCH|DELETE)' -retrieval: - aliases: - - next.js - - nextjs app - - react framework - - app router - intents: - - set up routing and layouts in a Next.js app - - choose between server and client components for a feature - - configure data fetching or caching in App Router - - add middleware or proxy logic to handle requests - - set up server rendering for React pages - - add a new page with dynamic route segments - entities: - - App Router - - Server Components - - Server Actions - - generateMetadata - - layout - - proxy - - next.config - examples: - - add a new page with dynamic routing - - should this be a server or client component - - set up middleware for auth redirects - - configure caching for this data fetch - - set up server rendering for my pages - diff --git a/skills/nextjs/references/app-router-files.md b/skills/nextjs/references/app-router-files.md deleted file mode 100644 index 0061932..0000000 --- a/skills/nextjs/references/app-router-files.md +++ /dev/null @@ -1,94 +0,0 @@ -# Next.js App Router — File Convention Reference - -## Special Files - -| File | Purpose | Server/Client | -|------|---------|---------------| -| `layout.tsx` | Shared UI wrapper, preserves state | Server (default) | -| `page.tsx` | Unique route UI | Server (default) | -| `loading.tsx` | Suspense fallback | Server (default) | -| `error.tsx` | Error boundary | Client (required) | -| `not-found.tsx` | 404 UI | Server (default) | -| `route.ts` | API endpoint (Route Handler) | Server only | -| `template.tsx` | Layout that remounts on navigation | Server (default) | -| `default.tsx` | Parallel route fallback | Server (default) | -| `proxy.ts` | Network proxy (replaces middleware) | Server (Node.js) | -| `opengraph-image.tsx` | Auto-generated OG image for route | Server (Edge) | -| `twitter-image.tsx` | Auto-generated Twitter card image | Server (Edge) | - -## Route Segments - -| Pattern | Example | Matches | -|---------|---------|---------| -| `[id]` | `app/users/[id]/page.tsx` | `/users/123` | -| `[...slug]` | `app/docs/[...slug]/page.tsx` | `/docs/a/b/c` | -| `[[...slug]]` | `app/shop/[[...slug]]/page.tsx` | `/shop` or `/shop/a/b` | -| `(group)` | `app/(marketing)/page.tsx` | `/` (group ignored in URL) | -| `@slot` | `app/@sidebar/page.tsx` | Parallel route slot | - -## Data Fetching Patterns - -### Server Component (Default) -```tsx -export default async function Page() { - const data = await fetch('https://api.example.com/data') - return
{data}
-} -``` - -### With Params (Async in Next.js 16) -```tsx -export default async function Page({ - params, -}: { - params: Promise<{ id: string }> -}) { - const { id } = await params - const user = await getUser(id) - return -} -``` - -### With Search Params (Async in Next.js 16) -```tsx -export default async function Page({ - searchParams, -}: { - searchParams: Promise<{ q?: string }> -}) { - const { q } = await searchParams - const results = await search(q) - return -} -``` - -### generateStaticParams (SSG) -```tsx -export async function generateStaticParams() { - const posts = await getPosts() - return posts.map((post) => ({ slug: post.slug })) -} - -export default async function Page({ - params, -}: { - params: Promise<{ slug: string }> -}) { - const { slug } = await params - const post = await getPost(slug) - return -} -``` - -### generateMetadata -```tsx -export async function generateMetadata({ - params, -}: { - params: Promise<{ id: string }> -}) { - const { id } = await params - const product = await getProduct(id) - return { title: product.name, description: product.description } -} -``` diff --git a/skills/nextjs/references/async-patterns.md b/skills/nextjs/references/async-patterns.md deleted file mode 100644 index dce8d8c..0000000 --- a/skills/nextjs/references/async-patterns.md +++ /dev/null @@ -1,87 +0,0 @@ -# Async Patterns - -In Next.js 15+, `params`, `searchParams`, `cookies()`, and `headers()` are asynchronous. - -## Async Params and SearchParams - -Always type them as `Promise<...>` and await them. - -### Pages and Layouts - -```tsx -type Props = { params: Promise<{ slug: string }> } - -export default async function Page({ params }: Props) { - const { slug } = await params -} -``` - -### Route Handlers - -```tsx -export async function GET( - request: Request, - { params }: { params: Promise<{ id: string }> } -) { - const { id } = await params -} -``` - -### SearchParams - -```tsx -type Props = { - params: Promise<{ slug: string }> - searchParams: Promise<{ query?: string }> -} - -export default async function Page({ params, searchParams }: Props) { - const { slug } = await params - const { query } = await searchParams -} -``` - -### Synchronous Components - -Use `React.use()` for non-async components: - -```tsx -import { use } from 'react' - -type Props = { params: Promise<{ slug: string }> } - -export default function Page({ params }: Props) { - const { slug } = use(params) -} -``` - -### generateMetadata - -```tsx -type Props = { params: Promise<{ slug: string }> } - -export async function generateMetadata({ params }: Props): Promise { - const { slug } = await params - return { title: slug } -} -``` - -## Async Cookies and Headers - -```tsx -import { cookies, headers } from 'next/headers' - -export default async function Page() { - const cookieStore = await cookies() - const headersList = await headers() - - const theme = cookieStore.get('theme') - const userAgent = headersList.get('user-agent') -} -``` - -## Migration Codemod - -```bash -npx @next/codemod@latest next-async-request-api . -``` diff --git a/skills/nextjs/references/bundling.md b/skills/nextjs/references/bundling.md deleted file mode 100644 index ac5e814..0000000 --- a/skills/nextjs/references/bundling.md +++ /dev/null @@ -1,180 +0,0 @@ -# Bundling - -Fix common bundling issues with third-party packages. - -## Server-Incompatible Packages - -Some packages use browser APIs (`window`, `document`, `localStorage`) and fail in Server Components. - -### Error Signs - -``` -ReferenceError: window is not defined -ReferenceError: document is not defined -ReferenceError: localStorage is not defined -Module not found: Can't resolve 'fs' -``` - -### Solution 1: Mark as Client-Only - -If the package is only needed on client: - -```tsx -// Bad: Fails - package uses window -import SomeChart from 'some-chart-library' - -export default function Page() { - return -} - -// Good: Use dynamic import with ssr: false -import dynamic from 'next/dynamic' - -const SomeChart = dynamic(() => import('some-chart-library'), { - ssr: false, -}) - -export default function Page() { - return -} -``` - -### Solution 2: Externalize from Server Bundle - -For packages that should run on server but have bundling issues: - -```js -// next.config.js -module.exports = { - serverExternalPackages: ['problematic-package'], -} -``` - -Use this for: -- Packages with native bindings (sharp, bcrypt) -- Packages that don't bundle well (some ORMs) -- Packages with circular dependencies - -### Solution 3: Client Component Wrapper - -Wrap the entire usage in a client component: - -```tsx -// components/ChartWrapper.tsx -'use client' - -import { Chart } from 'chart-library' - -export function ChartWrapper(props) { - return -} - -// app/page.tsx (server component) -import { ChartWrapper } from '@/components/ChartWrapper' - -export default function Page() { - return -} -``` - -## CSS Imports - -Import CSS files instead of using `` tags. Next.js handles bundling and optimization. - -```tsx -// Bad: Manual link tag - - -// Good: Import CSS -import './styles.css' - -// Good: CSS Modules -import styles from './Button.module.css' -``` - -## Polyfills - -Next.js includes common polyfills automatically. Don't load redundant ones from polyfill.io or similar CDNs. - -Already included: `Array.from`, `Object.assign`, `Promise`, `fetch`, `Map`, `Set`, `Symbol`, `URLSearchParams`, and 50+ others. - -```tsx -// Bad: Redundant polyfills - - -// Good: Next.js Script component -import Script from 'next/script' - - -``` - -## Don't Put Script in Head - -`next/script` should not be placed inside `next/head`. It handles its own positioning. - -```tsx -// Bad: Script inside Head -import Head from 'next/head' -import Script from 'next/script' - - - - -// Good: Next.js component -import { GoogleAnalytics } from '@next/third-parties/google' - -export default function Layout({ children }) { - return ( - - {children} - - - ) -} -``` - -## Google Tag Manager - -```tsx -import { GoogleTagManager } from '@next/third-parties/google' - -export default function Layout({ children }) { - return ( - - - {children} - - ) -} -``` - -## Other Third-Party Scripts - -```tsx -// YouTube embed -import { YouTubeEmbed } from '@next/third-parties/google' - - - -// Google Maps -import { GoogleMapsEmbed } from '@next/third-parties/google' - - -``` - -## Quick Reference - -| Pattern | Issue | Fix | -|---------|-------|-----| -| ` - -// Good: Next.js Script component -import Script from 'next/script' - - -``` - -## Don't Put Script in Head - -`next/script` should not be placed inside `next/head`. It handles its own positioning. - -```tsx -// Bad: Script inside Head -import Head from 'next/head' -import Script from 'next/script' - - - - -// Good: Next.js component -import { GoogleAnalytics } from '@next/third-parties/google' - -export default function Layout({ children }) { - return ( - - {children} - - - ) -} -``` - -## Google Tag Manager - -```tsx -import { GoogleTagManager } from '@next/third-parties/google' - -export default function Layout({ children }) { - return ( - - - {children} - - ) -} -``` - -## Other Third-Party Scripts - -```tsx -// YouTube embed -import { YouTubeEmbed } from '@next/third-parties/google' - - - -// Google Maps -import { GoogleMapsEmbed } from '@next/third-parties/google' - - -``` - -## Quick Reference - -| Pattern | Issue | Fix | -|---------|-------|-----| -| `