{"manifest":{"name":"TypeScript Next.js Rules","version":"1.0.0","description":"Context rules for AI agents working in TypeScript/Next.js App Router projects. Covers strict typing, server vs client components, Tailwind, Zod, and file conventions.","tags":["typescript","nextjs","react","rules","context","tailwind"],"standard":"agentskills.io","standard_version":"1.0","content_checksum":"a2f721041df9db11c13bc515eb72561adac0feb094816e79c7001ee12a85ca34","bundle_checksum":null,"metadata":{},"files":[]},"files":{"SKILL.md":"# TypeScript Next.js Rules\n\n> **Context skill:** Install this into your agent to enforce consistent, production-quality patterns in TypeScript Next.js App Router projects. Works with Claude Code, Cursor, Windsurf, and any agent that reads context files.\n\n---\n\n## Stack Assumptions\n\n- **Next.js 14+** with App Router (not Pages Router)\n- **TypeScript** with strict mode enabled (`\"strict\": true` in tsconfig)\n- **Tailwind CSS** for styling\n- **Zod** for runtime validation\n- **Supabase or Prisma** for database (adapt as needed)\n\n---\n\n## File & Directory Conventions\n\n```\napp/\n  (group)/          ← Route groups (no URL segment)\n    layout.tsx      ← Shared layout\n    page.tsx        ← Route page (server component by default)\n    loading.tsx     ← Suspense fallback\n    error.tsx       ← Error boundary ('use client' required)\n  api/\n    route.ts        ← API routes (GET, POST, etc. as named exports)\ncomponents/\n  ui/               ← Primitive UI: Button, Card, Badge, etc.\n  feature/          ← Feature-specific composed components\nlib/\n  utils/            ← Pure utility functions (no React)\n  types/            ← TypeScript type definitions\n  actions/          ← Server Actions\nhooks/              ← Custom React hooks (client-side)\n```\n\n---\n\n## Server vs Client Components\n\n**Default to Server Components.** Only add `'use client'` when you need:\n- `useState` or `useReducer`\n- `useEffect` or lifecycle hooks\n- Browser APIs (`window`, `localStorage`, etc.)\n- Event handlers that run in the browser\n- Third-party client-only libraries\n\n**Never** put `'use client'` at the top of a layout file.\n\n---\n\n## TypeScript Rules\n\n```typescript\n// ✅ Always type function return values explicitly\nasync function getUser(id: string): Promise<User | null> { ... }\n\n// ✅ Use `type` for unions/intersections, `interface` for object shapes\ntype Status = 'pending' | 'active' | 'archived'\ninterface UserProfile { id: string; email: string; }\n\n// ✅ Never use `any` — use `unknown` and narrow\nfunction parse(input: unknown): string {\n  if (typeof input !== 'string') throw new Error('Expected string')\n  return input\n}\n\n// ❌ Never\nconst data: any = response.json()\n\n// ✅ Type API responses with Zod\nconst UserSchema = z.object({ id: z.string().uuid(), email: z.string().email() })\ntype User = z.infer<typeof UserSchema>\n```\n\n---\n\n## API Routes\n\n```typescript\n// app/api/users/route.ts\nimport { NextResponse } from 'next/server'\nimport { z } from 'zod'\n\nconst CreateUserSchema = z.object({\n  email: z.string().email(),\n  name: z.string().min(1).max(100),\n})\n\nexport async function POST(request: Request) {\n  const body = await request.json()\n  const result = CreateUserSchema.safeParse(body)\n  if (!result.success) {\n    return NextResponse.json({ error: result.error.flatten() }, { status: 422 })\n  }\n  // ... proceed with result.data (fully typed)\n}\n```\n\n---\n\n## Data Fetching\n\n```typescript\n// ✅ Server Component data fetching — no useEffect\nexport default async function UsersPage() {\n  const users = await getUsers() // Direct async call in component\n  return <UserList users={users} />\n}\n\n// ✅ Parallel fetching with Promise.all\nconst [user, posts] = await Promise.all([getUser(id), getPosts(id)])\n\n// ✅ Use `cache` for request deduplication\nimport { cache } from 'react'\nconst getUser = cache(async (id: string) => { ... })\n```\n\n---\n\n## Error Handling\n\n```typescript\n// ✅ Never throw in Server Components — use error.tsx\n// app/(main)/error.tsx\n'use client'\nexport default function Error({ error, reset }: { error: Error; reset: () => void }) {\n  return <div><h2>Something went wrong</h2><button onClick={reset}>Try again</button></div>\n}\n\n// ✅ API routes always return typed error shapes\nreturn NextResponse.json({ error: 'Not found', code: 'USER_NOT_FOUND' }, { status: 404 })\n```\n\n---\n\n## Tailwind CSS\n\n- Use Tailwind utility classes directly — no custom CSS unless absolutely necessary\n- Use `cn()` helper (from `clsx` + `tailwind-merge`) to conditionally join classes\n- Dark mode via `dark:` variants (not separate stylesheets)\n- Never use inline `style={{}}` for values that Tailwind covers\n- Use `@apply` sparingly — only in component-level `.module.css` files if needed\n\n---\n\n## Do Not\n\n- Do not use `pages/` directory — App Router only\n- Do not import server-only code into client components (will cause runtime errors)\n- Do not use `getServerSideProps` or `getStaticProps` — use async Server Components\n- Do not `console.log` in production code — use a proper logger\n- Do not hardcode environment variables — use `process.env.VAR` with validation on startup\n- Do not use default exports for utility functions — named exports only\n\n## Playground\n\n<!DOCTYPE html><html><head><meta charset='utf-8'><style>*{box-sizing:border-box;margin:0;padding:0}body{background:#0d1117;font-family:monospace;font-size:11px;height:100vh;display:flex;flex-direction:column;overflow:hidden;padding:12px}.title{color:#58a6ff;font-size:13px;font-weight:bold;margin-bottom:6px}.subtitle{color:#8b949e;font-size:10px;margin-bottom:10px}.rule{display:flex;align-items:flex-start;gap:8px;margin-bottom:7px;line-height:1.5}.dot{color:#3fb950;font-size:14px;line-height:1.3;flex-shrink:0}.text{color:#e6edf3}.hl{color:#3fb950}.section{color:#e3b341;font-size:10px;text-transform:uppercase;letter-spacing:.08em;margin:10px 0 5px}</style></head><body><div class='title'>TypeScript · Next.js Rules</div><div class='subtitle'>Active in every session — enforced automatically</div><div class='section'>Types</div><div class='rule'><span class='dot'>●</span><div class='text'>No <span class='hl'>any</span> — use <span class='hl'>unknown</span> with narrowing or explicit generics</div></div><div class='rule'><span class='dot'>●</span><div class='text'>Prefer <span class='hl'>interface</span> for object shapes, <span class='hl'>type</span> for unions/aliases</div></div><div class='rule'><span class='dot'>●</span><div class='text'>All <span class='hl'>async</span> functions return <span class='hl'>Promise&lt;T&gt;</span> — never implicit any</div></div><div class='section'>Next.js</div><div class='rule'><span class='dot'>●</span><div class='text'>Server Components by default — add <span class='hl'>'use client'</span> only when needed</div></div><div class='rule'><span class='dot'>●</span><div class='text'>Data fetching in Server Components via <span class='hl'>fetch()</span> with cache options</div></div><div class='rule'><span class='dot'>●</span><div class='text'>API routes: validate input with <span class='hl'>zod</span> before touching the DB</div></div><div class='section'>Style</div><div class='rule'><span class='dot'>●</span><div class='text'>Tailwind only — no inline styles, no CSS modules</div></div><div class='rule'><span class='dot'>●</span><div class='text'>Imports: external → internal → relative (enforced by ESLint)</div></div></body></html>"}}