diff --git a/.agents/skills/next-best-practices/SKILL.md b/.agents/skills/next-best-practices/SKILL.md deleted file mode 100644 index 437896b4..00000000 --- a/.agents/skills/next-best-practices/SKILL.md +++ /dev/null @@ -1,153 +0,0 @@ ---- -name: next-best-practices -description: Next.js best practices - file conventions, RSC boundaries, data patterns, async APIs, metadata, error handling, route handlers, image/font optimization, bundling -user-invocable: false ---- - -# 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/.agents/skills/next-best-practices/async-patterns.md b/.agents/skills/next-best-practices/async-patterns.md deleted file mode 100644 index dce8d8cc..00000000 --- a/.agents/skills/next-best-practices/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/.agents/skills/next-best-practices/bundling.md b/.agents/skills/next-best-practices/bundling.md deleted file mode 100644 index ac5e814c..00000000 --- a/.agents/skills/next-best-practices/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 | -|---------|-------|-----| -| ` - -// 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 | -|---------|-------|-----| -| `