DEV Community

eddylee
eddylee

Posted on

Why I Built a 4,000-Line Agent Skill Instead of Another npm Package

The Problem

I use Claude Code (and sometimes Cursor) for frontend work every day. And every day, I fix the same mistakes:

// AI generates this
const user: User = await res.json()
Enter fullscreen mode Exit fullscreen mode

Looks fine. TypeScript is happy. But res.json() returns any at runtime — if the API changes shape, this silently breaks in production.

// AI also loves this
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<Error | null>(null)
const [data, setData] = useState<User | null>(null)
Enter fullscreen mode Exit fullscreen mode

Three separate pieces of state that can represent impossible combinations. isLoading: true AND data present? error set but isLoading still true?

And my personal favorite:

'use client' // slapped on the page component

export default function ProductPage() {
  // ...entire page is now client-rendered
}
Enter fullscreen mode Exit fullscreen mode

These aren't obscure edge cases. They happen constantly because AI agents don't have a structured reference for frontend TypeScript patterns.

Why Not Just Fix It Each Time?

I did. For months. Then I realized:

  • I'm correcting the same patterns over and over
  • My corrections aren't persisted between sessions
  • Every new conversation starts from zero

I needed something the agent could read before generating code — not a tutorial I'd paste into chat, but a structured reference it would consult automatically.

What I Built

typescript-react-patterns — an Agent Skill for Claude Code, Cursor, Codex, and any AI tool that reads SKILL.md.

17 files. 4,000+ lines. Three directories:

typescript-react-patterns/
├── SKILL.md              ← Hub: agent rules, decision guide, checklists
├── rules/                ← 11 pattern modules
│   ├── typescript-core.md
│   ├── react-typescript-patterns.md
│   ├── nextjs-typescript.md
│   ├── component-patterns.md
│   ├── data-fetching-and-api-types.md
│   ├── forms-and-validation.md
│   ├── state-management.md
│   ├── performance-and-accessibility.md
│   ├── debugging-checklists.md
│   ├── code-review-rules.md
│   └── anti-patterns.md
└── playbooks/            ← 3 debugging guides
    ├── type-error-debugging.md
    ├── hydration-issues.md
    └── effect-dependency-bugs.md
Enter fullscreen mode Exit fullscreen mode

What Makes This Different

I've seen a lot of agent skills. Most are collections of code snippets. This one is designed as decision support — helping the agent choose the right pattern, not just showing patterns.

1. Agent Behavior Rules

The skill starts by telling the agent what to check before writing any code:

  • Is this server or client code?
  • Is runtime validation needed? (Yes, if data comes from outside the app)
  • What Next.js version? (params is a Promise in 15+)
  • What assumptions must NOT be made?

2. Decision Flowcharts

Not just "here's a pattern" — but "when to use which":

Is this data from a server/API?
├─ Yes → TanStack Query (NOT useState)
└─ No → Is it shareable via URL?
   ├─ Yes → searchParams
   └─ No → How many components need it?
      ├─ 1 → useState
      └─ Many → Zustand with selectors
Enter fullscreen mode Exit fullscreen mode

3. Rule Classification

Every rule is labeled:

  • [HARD RULE] — Violating causes bugs. No exceptions. "Validate API responses at runtime."
  • [DEFAULT] — Recommended unless you have a documented reason. "Use interface for Props."
  • [SITUATIONAL] — Depends on context. "Polymorphic components — only for design-system foundations."

4. Before/After That Actually Matter

Not toy examples. Real frontend scenarios:

API typing:

// ❌ Before
const user: User = await res.json()

// ✅ After
const userSchema = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string().email(),
})
type User = z.infer<typeof userSchema>
const user = userSchema.parse(await res.json())
Enter fullscreen mode Exit fullscreen mode

Loading state:

// ❌ Before — impossible states are representable
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState(null)
const [data, setData] = useState(null)

// ✅ After — impossible states are unrepresentable
type State<T> =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: T }
  | { status: 'error'; error: Error }
Enter fullscreen mode Exit fullscreen mode

5. Debugging Playbooks

When something goes wrong, the agent has step-by-step diagnosis guides:

  • Type errors: Read bottom-up, classify, check common React/Next.js-specific errors
  • Hydration mismatches: Flowchart from symptom to fix (useEffect vs dynamic vs Suspense)
  • useEffect bugs: Infinite loops (unstable deps), stale closures (captured state), missing cleanup

6. Code Review Heuristics

The skill distinguishes risk (flag it) from preference (mention it, don't block):

Risk: as on API data, useEffect with object deps, server-only import in client component

Preference: type vs interface, handler naming convention, import ordering

How to Use It

One command:

git clone https://github.com/leejpsd/typescript-react-patterns ~/.claude/skills/typescript-react-patterns
Enter fullscreen mode Exit fullscreen mode

The agent reads SKILL.md automatically and consults the relevant rules/ file based on context. If you're working on a form, it reads forms-and-validation.md. If there's a type error, it reads playbooks/type-error-debugging.md.

Works with Claude Code, Cursor, Codex, Gemini CLI — anything that reads the SKILL.md format.

What's Covered

Module Topics
TypeScript Core Narrowing, generics, utility types, as const, satisfies, unknown vs any
React Patterns Props, children, events, hooks, context, forwardRef, generic components
Next.js App Router params, Server Actions, RSC boundaries, Edge, useOptimistic
Component Patterns Discriminated Props, compound components, modal/dialog, polymorphic
Data Fetching Zod validation, TanStack Query, Result<T,E>, pagination, error handling
Forms react-hook-form + Zod, Server Actions, multi-step checkout example
State Management Decision matrix, Zustand (+ middleware), Context, URL state
Performance & A11y Memoization tradeoffs, focus management, aria-live, keyboard navigation
Anti-patterns 12 mistakes with symptoms, root causes, and fixes

What I Learned Building This

1. Structure matters more than volume.

Early versions had more files but less structure. The current version has fewer, denser modules with a consistent template: Scope → Key Rules → Examples → Anti-patterns → Review Checklist.

2. Agent skills need decision support, not just examples.

Showing 10 patterns is less useful than helping the agent choose between them. Flowcharts and decision matrices are more valuable than code snippets.

3. Classify your rules.

[HARD RULE] vs [DEFAULT] vs [SITUATIONAL] changed everything. The agent stops treating every guideline as absolute.

4. Cross-references prevent duplication.

Every file has See also links. The agent can navigate between modules without each file repeating everything.

Contributing

The skill is MIT-licensed and PRs are welcome. Priority areas:

  • Testing patterns (Vitest, Testing Library)
  • Internationalization typing
  • More debugging playbooks
  • Accessibility deep dive

If you try it and something is wrong or missing, open an issue. This is built to be iterated on.

Links

If this is useful, a ⭐ on GitHub helps others find it.

Top comments (0)