Typescript react nextjs patterns
Production-grade Agent Skill for TypeScript, React & Next.js — decision guides, hard rules, and debugging playbooks for AI coding agents (Claude Code, Cursor, Codex)
npx -y skills add leejpsd/typescript-react-nextjs-patternsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 16 stars16 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Production-grade TypeScript reference for React & Next.js frontend development. Covers type narrowing, component Props, generic hooks, discriminated unions, as const, satisfies, Zod validation, TanStack Query, server/client boundaries, forms, state management, performance, accessibility, debugging, and code review. Use when the user works with TypeScript in React or Next.js: type errors, Props design, generics, API typing, SSR/CSR boundaries, hydration issues, form validation, state management, performance, or code review. Also use for "how should I type this?", "why does this type error happen?", or any architectural decision involving TypeScript in a frontend context.
SKILL.md
10.3 KB, as published. Nobody here has run it
TypeScript for React & Next.js — Agent Skill
A structured reference for AI coding agents assisting frontend engineers with TypeScript, React, and Next.js in production environments.
Agent Behavior Rules
Before answering, always verify:
- Server or client? Server Components, Server Actions, and Route Handlers have different type constraints than
"use client"components. - Runtime validation needed? Static types do NOT validate API responses, URL params, form data, or localStorage. Data crossing a trust boundary requires Zod or equivalent.
- App Router or Pages Router? Patterns differ significantly. If unclear, ask.
- TypeScript version?
satisfiesneeds 5.0+,NoInfer5.4+, inferred type predicates 5.5+. TS 6.0 is the final JS-based compiler; TS 7 is the Go-native rewrite — ~10x faster, same language, but tooling that consumes the old compiler API may lag. - Next.js version?
paramsis aPromisein 15+. Caching model changed in 16+ ("use cache"), and 16 renamesmiddleware.ts→proxy.ts(Node runtime). - React version? 19 makes
refa regular prop (forwardRefdeprecated), renders<Context>directly, and replacesuseFormStatewithuseActionState.useEffectEventand<Activity>need 19.2+.
Assumptions the agent must NOT make:
- That API responses match their TypeScript types at runtime
- That
searchParamsvalues are the expected type (they are alwaysstring | string[] | undefined) - That
anyin existing code is intentional - That a type assertion (
as) is justified without checking context - That server-only imports are safe in client components
- That
useEffectdependencies in existing code are correct
When uncertain:
- State tradeoffs explicitly rather than picking one approach silently
- Mark unstable or version-dependent patterns as such
- Distinguish: [HARD RULE] (violating causes bugs) / [DEFAULT] (override with reason) / [SITUATIONAL] (depends on context)
After context compaction:
If this session was compacted and the rules content survives only as a summary:
- Do NOT answer from the summary — it drops the reasoning, examples, and edge cases.
- Re-read the relevant file from the Decision Guide below before continuing. One file read is enough; this hub is deliberately thin so recovery is cheap.
- If mid-task and unsure which file applies, re-read
HARD-RULES.mdfirst — every non-negotiable rule in compressed form.
Decision Guide
Quick: What pattern should I use?
| Situation | Start here |
|---|---|
| Typing component Props, children, events, refs | → react-typescript-patterns.md |
Narrowing unions, unknown, type guards, utility types | → typescript-core.md |
| Next.js params, searchParams, Server Actions, RSC boundary | → nextjs-typescript.md |
| Discriminated unions, conditional props, compound components | → component-patterns.md |
| API responses, fetch typing, TanStack Query, caching | → data-fetching-and-api-types.md |
| Form state, validation, controlled vs uncontrolled | → forms-and-validation.md |
| Local state vs context vs server state vs Zustand | → state-management.md |
| Re-renders, memoization, accessibility | → performance-and-accessibility.md |
| Type errors, hydration, stale state, effect bugs | → debugging-checklists.md (hub) + playbooks/ |
| PR review, risk vs preference, architecture smells | → code-review-rules.md |
| Common mistakes, cargo-cult patterns | → anti-patterns.md |
Flowchart: Is this data safe to use?
Data comes from...
├─ Inside the app (useState, useReducer, computed)
│ → Static typing is sufficient. No runtime validation needed.
│
├─ Outside the app (API, URL, FormData, localStorage, postMessage)
│ → [HARD RULE] Validate at runtime. Use Zod or equivalent.
│ │
│ ├─ API response → schema.parse(await res.json())
│ ├─ URL params → schema.parse(searchParams)
│ ├─ FormData → schema.safeParse({ field: formData.get('field') })
│ ├─ localStorage → schema.safeParse(JSON.parse(stored))
│ └─ postMessage → schema.safeParse(event.data)
│
└─ Third-party library callback
→ Check library types. Add runtime guard if types seem wrong.
Flowchart: Where should this state live?
Is this data from a server/API?
├─ Yes → TanStack Query (NOT useState). See data-fetching-and-api-types.md
│
└─ No → Is it shareable via URL? (filters, page, sort)
├─ Yes → searchParams or nuqs. See state-management.md
│
└─ No → How many components need it?
├─ 1 component → useState or useReducer
├─ 2-3 in same tree → Lift state up (props)
└─ Many across trees → How often does it change?
├─ Rarely (theme, locale, auth) → Context
└─ Often (cart, notifications) → Zustand with selectors
Flowchart: Should I memoize this?
Is there a measured performance problem?
├─ No → Don't memoize. Stop here.
│
└─ Yes → Can you restructure instead?
├─ Yes → Move state down, extract components. See performance-and-accessibility.md
│
└─ No → What needs memoizing?
├─ Expensive computation → useMemo (verify it's truly expensive)
├─ Callback to memoized child → useCallback
└─ Component in a long list → React.memo (verify props are stable)
Quick: hard rule vs default vs situational
| Label | Meaning | Example |
|---|---|---|
| [HARD RULE] | Violating causes bugs or security issues. No exceptions. | "Validate API responses at runtime" |
| [DEFAULT] | Recommended unless you have a documented reason to deviate. | "Use interface for Props" |
| [SITUATIONAL] | Depends on context. Both options are valid. Explain your choice. | "Polymorphic components — only for design-system foundations" |
Code Generation Checklist
Before generating TypeScript/React/Next.js code:
Context
- Confirmed: server or client code?
- Confirmed: App Router or Pages Router?
- Confirmed: TypeScript strict mode enabled?
Type Safety
- No
any— useunknownwith validation or proper types - No
aswithout documented justification - External data (API, URL, form, storage) validated at runtime
- Props use
interface, only truly optional fields have?
React
-
childrentyped asReact.ReactNode - Event handler Props expose values, not event objects
- Effects have stable dependencies and cleanup functions
-
"use client"only where needed, as deep as possible - No server data duplicated into
useState
Next.js (15+)
-
paramsandsearchParamsawaited - Server Actions validate FormData with Zod
- Sensitive code protected with
import 'server-only' - Cross-boundary Props are RSC-serializable (no plain callbacks or class instances;
Date/Map/Set/Promiseare fine)
Accessibility
- Form inputs have associated labels
- Error messages use
role="alert" - Interactive elements are keyboard-accessible
Code Review Checklist
Flag as risk (likely bug or maintenance problem)
anywithout documented reasonason external data without validation!non-null assertion without prior guarduseEffectwith object/array dependencies (likely unstable)- Missing
useEffectcleanup - Server data copied into
useState "use client"at page/layout level- Non-Server-Function callbacks or class instances passed across server/client boundary
params/searchParamsnot awaited (Next.js 15+)- Server Action without FormData validation
Flag as preference (mention, don't block)
typevsinterfacefor object shapes- Handler naming convention
- File/folder organization style
- Import ordering
File Index
| File | Scope |
|---|---|
HARD-RULES.md | All [HARD RULE]s in one page — compaction recovery, CLAUDE.md embedding, hook injection |
typescript-core.md | Narrowing, unions, generics, utility types, inference, unknown vs any, as const, satisfies |
react-typescript-patterns.md | Props, children, events, refs (19 ref-as-prop / 18 forwardRef), hooks, context |
nextjs-typescript.md | App Router types, params, searchParams, Server Actions, RSC boundaries, Cache Components (16), proxy.ts, metadata |
component-patterns.md | Discriminated union Props, compound components, controlled/uncontrolled, polymorphic |
data-fetching-and-api-types.md | Fetch typing, Zod validation, TanStack Query, safe response handling |
forms-and-validation.md | Form state, Zod, react-hook-form, Server Actions, progressive enhancement |
state-management.md | Local state, Context, Zustand, TanStack Query, URL state, decision matrix |
performance-and-accessibility.md | Memoization tradeoffs, effect stability, semantic HTML, ARIA patterns |
debugging-checklists.md | Quick diagnosis router, serialization issues, null access, re-render errors |
code-review-rules.md | Risk vs preference, architecture smells, review comment templates |
anti-patterns.md | 13 common mistakes with root causes and fixes |
playbooks/ — Step-by-step debugging guides (consult when diagnosing specific bugs)
| File | Scope |
|---|---|
type-error-debugging.md | Systematic type error resolution with React/Next.js-specific errors |
hydration-issues.md | SSR/CSR mismatch diagnosis flowchart and fix patterns |
effect-dependency-bugs.md | Infinite loops, stale closures, missing cleanups, real-world debounce example |