Skip to main content
skill CX Codex

react-dev — Claude Code skill: what it does, how to install

Provides type-safe React 18 and 19 patterns for TypeScript components, hooks, and Server Actions.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add softaworks/agent-toolkit --skill react-dev -a claude-code
Open on GitHub

What react-dev does

The skill equips the agent with strict TypeScript patterns for React 18 and 19. It covers generic components, discriminated union props, and event handlers such as React.FormEvent or React.MouseEvent without falling back on loose annotations.

It guides migrations to React 19 breaking changes. You get ready patterns for passing ref directly as a prop instead of using deprecated forwardRef, handling form submissions with useActionState, and resolving promises with the use() function alongside Server Actions.

This rule set breaks down immediately in plain JavaScript or non-React codebases because it expects TypeScript. If your project lacks TypeScript, loading this skill will only inject unwanted type constructs.

What it helps with

  • type generic React components without manual call-site annotations
  • migrate components from forwardRef to React 19 ref props
  • wire useActionState with typed form state and Server Actions
  • handle React form and mouse events with accurate target types

Example prompts for the agent

  • Create a generic table component with keyof T column headers
  • Migrate this button component to React 19 ref as prop
  • Set up useActionState for this form submission handler

How to install react-dev

1Skills CLI (Vercel)

npx skills add softaworks/agent-toolkit --skill react-dev -a claude-code

2Manually: put the files in place

~/.claude/skills/react-dev/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/react-dev/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about react-dev

How to install react-dev in Claude Code?

Run npx skills add softaworks/agent-toolkit --skill react-dev -a claude-code. This command imports the skill directly into your agent setup.

Does react-dev support plain JavaScript projects?

No. The guidelines strictly target React 18–19 with TypeScript. It is explicitly not intended for vanilla JavaScript or non-React code.

What React 19 updates are covered?

It covers direct ref props without forwardRef, replacing useFormState with useActionState, unwrapping promises via use(), and typing async Server Components.

Source text

SKILL.md · MIT
---
name: react-dev
version: 1.0.0
description: This skill should be used when building React components with TypeScript, typing hooks, handling events, or when React TypeScript, React 19, Server Components are mentioned. Covers type-safe patterns for React 18-19 including generic components, proper event typing, and routing integration (TanStack Router, React Router).
---

# React TypeScript

Type-safe React = compile-time guarantees = confident refactoring.

<when_to_use>

- Building typed React components
- Implementing generic components
- Typing event handlers, forms, refs
- Using React 19 features (Actions, Server Components, use())
- Router integration (TanStack Router, React Router)
- Custom hooks with proper typing

NOT for: non-React TypeScript, vanilla JS React

</when_to_use>

<react_19_changes>

React 19 breaking changes require migration. Key patterns:

**ref as prop** - forwardRef deprecated:

```typescript
// React 19 - ref as regular prop
type ButtonProps = {
  ref?: React.Ref<HTMLButtonElement>;
} & React.ComponentPropsWithoutRef<'button'>;

function Button({ ref, children, ...props }: ButtonProps) {
  return <button ref={ref} {...props}>{children}</button>;
}
```

**useActionState** - replaces useFormState:

```typescript
import { useActionState } from 'react';

type FormState = { errors?: string[]; success?: boolean };

function Form() {
  const [state, formAction, isPending] = useActionState(submitAction, {});
  return <form action={formAction}>...</form>;
}
```

**use()** - unwraps promises/context:

```typescript
function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
  const user = use(userPromise); // Suspends until resolved
  return <div>{user.name}</div>;
}
```

See [react-19-patterns.md](references/react-19-patterns.md) for useOptimistic, useTransition, migration checklist.

</react_19_changes>

<component_patterns>

**Props** - extend native elements:

```typescript
type ButtonProps = {
  variant: 'primary' | 'secondary';
} & React.ComponentPropsWithoutRef<'button'>;

function Button({ variant, children, ...props }: ButtonProps) {
  return <button className={variant} {...props}>{children}</button>;
}
```

**Children typing**:

```typescript
type Props = {
  children: React.ReactNode;          // Anything renderable
  icon: React.ReactElement;           // Single element
  render: (data: T) => React.ReactNode;  // Render prop
};
```

**Discriminated unions** for variant props:

```typescript
type ButtonProps =
  | { variant: 'link'; href: string }
  | { variant: 'button'; onClick: () => void };

function Button(props: ButtonProps) {
  if (props.variant === 'link') {
    return <a href={props.href}>Link</a>;
  }
  return <button onClick={props.onClick}>Button</button>;
}
```

</component_patterns>

<event_handlers>

Use specific event types for accurate target typing:

```typescript
// Mouse
function handleClick(e: React.MouseEvent<HTMLButtonElement>) {
  e.currentTarget.disabled = true;
}

// Form
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
  e.preventDefault();
  const formData = new FormData(e.currentTarget);
}

// Input
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
  console.log(e.target.value);
}

// Keyboard
function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
  if (e.key === 'Enter') e.currentTarget.blur();
}
```

See [event-handlers.md](references/event-handlers.md) for focus, drag, clipboard, touch, wheel events.

</event_handlers>

<hooks_typing>

**useState** - explicit for unions/null:

```typescript
const [user, setUser] = useState<User | null>(null);
const [status, setStatus] = useState<'idle' | 'loading'>('idle');
```

**useRef** - null for DOM, value for mutable:

```typescript
const inputRef = useRef<HTMLInputElement>(null);  // DOM - use ?.
const countRef = useRef<number>(0);               // Mutable - direct access
```

**useReducer** - discriminated unions for actions:

```typescript
type Action =
  | { type: 'increment' }
  | { type: 'set'; payload: number };

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case 'set': return { ...state, count: action.payload };
    default: return state;
  }
}
```

**Custom hooks** - tuple returns with as const:

```typescript
function useToggle(initial = false) {
  const [value, setValue] = useState(initial);
  const toggle = () => setValue(v => !v);
  return [value, toggle] as const;
}
```

**useContext** - null guard pattern:

```typescript
const UserContext = createContext<User | null>(null);

function useUser() {
  const user = useContext(UserContext);
  if (!user) throw new Error('useUser outside UserProvider');
  return user;
}
```

See [hooks.md](references/hooks.md) for useCallback, useMemo, useImperativeHandle, useSyncExternalStore.

</hooks_typing>

<generic_components>

Generic components infer types from props - no manual annotations at call site.

**Pattern** - keyof T for column keys, render props for custom rendering:

```typescript
type Column<T> = {
  key: keyof T;
  header: string;
  render?: (value: T[keyof T], item: T) => React.ReactNode;
};

type TableProps<T> = {
  data: T[];
  columns: Column<T>[];
  keyExtractor: (item: T) => string | number;
};

function Table<T>({ data, columns, keyExtractor }: TableProps<T>) {
  return (
    <table>
      <thead>
        <tr>{columns.map(col => <th key={String(col.key)}>{col.header}</th>)}</tr>
      </thead>
      <tbody>
        {data.map(item => (
          <tr key={keyExtractor(item)}>
            {columns.map(col => (
              <td key={String(col.key)}>
                {col.render ? col.render(item[col.key], item) : String(item[col.key])}
              </td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
}
```

**Constrained generics** for required properties:

```typescript
type HasId = { id: string | number };

function List<T extends HasId>({ items }: { items: T[] }) {
  return <ul>{items.map(item => <li key={item.id}>...</li>)}</ul>;
}
```

See [generic-components.md](examples/generic-components.md) for Select, List, Modal, FormField patterns.

</generic_components>

<server_components>

React 19 Server Components run on server, can be async.

**Async data fetching**:

```typescript
export default async function UserPage({ params }: { params: { id: string } }) {
  const user = await fetchUser(params.id);
  return <div>{user.name}</div>;
}
```

**Server Actions** - 'use server' for mutations:

```typescript
'use server';

export async function updateUser(userId: string, formData: FormData) {
  await db.user.update({ where: { id: userId }, data: { ... } });
  revalidatePath(`/users/${userId}`);
}
```

**Client + Server Action**:

```typescript
'use client';

import { useActionState } from 'react';
import { updateUser } from '@/actions/user';

function UserForm({ userId }: { userId: string }) {
  const [state, formAction, isPending] = useActionState(
    (prev, formData) => updateUser(userId, formData), {}
  );
  return <form action={formAction}>...</form>;
}
```

**use() for promise handoff**:

```typescript
// Server: pass promise without await
async function Page() {
  const userPromise = fetchUser('123');
  return <UserProfile userPromise={userPromise} />;
}

// Client: unwrap with use()
'use client';
function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
  const user = use(userPromise);
  return <div>{user.name}</div>;
}
```

See [server-components.md](examples/server-components.md) for parallel fetching, streaming, error boundaries.

</server_components>

<routing>

Both TanStack Router and React Router v7 provide type-safe routing solutions.

**TanStack Router** - Compile-time type safety with Zod validation:

```typescript
import { createRoute } from '@tanstack/react-router';
import { z } from 'zod';

const userRoute = createRoute({
  path: '/users/$userId',
  component: UserPage,
  loader: async ({ params }) => ({ user: await fetchUser(params.userId) }),
  validateSearch: z.object({
    tab: z.enum(['profile', 'settings']).optional(),
    page: z.number().int().positive().default(1),
  }),
});

function UserPage() {
  const { user } = useLoaderData({ from: userRoute.id });
  const { tab, page } = useSearch({ from: userRoute.id });
  const { userId } = useParams({ from: userRoute.id });
}
```

**React Router v7** - Automatic type generation with Framework Mode:

```typescript
import type { Route } from "./+types/user";

export async function loader({ params }: Route.LoaderArgs) {
  return { user: await fetchUser(params.userId) };
}

export default function UserPage({ loaderData }: Route.ComponentProps) {
  const { user } = loaderData; // Typed from loader
  return <h1>{user.name}</h1>;
}
```

See [tanstack-router.md](references/tanstack-router.md) for TanStack patterns and [react-router.md](references/react-router.md) for React Router patterns.

</routing>

<rules>

ALWAYS:
- Specific event types (MouseEvent, ChangeEvent, etc)
- Explicit useState for unions/null
- ComponentPropsWithoutRef for native element extension
- Discriminated unions for variant props
- as const for tuple returns
- ref as prop in React 19 (no forwardRef)
- useActionState for form actions
- Type-safe routing patterns (see routing section)

NEVER:
- any for event handlers
- JSX.Element for children (use ReactNode)
- forwardRef in React 19+
- useFormState (deprecated)
- Forget null handling for DOM refs
- Mix Server/Client components in same file
- Await promises when passing to use()

</rules>

<references>

- [hooks.md](references/hooks.md) - useState, useRef, useReducer, useContext, custom hooks
- [event-handlers.md](references/event-handlers.md) - all event types, generic handlers
- [react-19-patterns.md](references/react-19-patterns.md) - useActionState, use(), useOptimistic, migration
- [generic-components.md](examples/generic-components.md) - Table, Select, List, Modal patterns
- [server-components.md](examples/server-components.md) - async components, Server Actions, streaming
- [tanstack-router.md](references/tanstack-router.md) - TanStack Router typed routes, search params, navigation
- [react-router.md](references/react-router.md) - React Router v7 loaders, actions, type generation, forms

</references>

© softaworks · MIT · softaworks/agent-toolkit

Similar skills

Everything for Frontend and UI →

From the same set12

database-schema-designer Design SQL and NoSQL data models with relationships, constraints, indexes, and migrations. Useful when starting a schema or reviewing one that already exists. CX Codex +1 4,300installs qa-test-planner Turns a feature description into a test plan, manual test cases, a regression suite or a bug report. Fires only when you name it: qa-test-planner. CX Codex +1 4,300installs session-handoff Creates handoff documents for AI agent session transfers. Useful when context window fills up or projects span multiple sessions. CX Codex +1 4,200installs codex A skill that runs Codex CLI for code analysis, refactoring, and automated editing. Uses GPT-5.2 by default with adaptive reasoning, hitting 76.3% on SWE-bench. CX Codex +1 4,100installs feedback-mastery Guides developers and leads through difficult workplace conversations using the SBI and Preparation-Delivery-Follow-up frameworks. Helps turn friction into factual feedback. CX Codex +1 4,000installs gemini Tells your agent how to drive Gemini CLI for code review, plan review, or files too large for its own context. Useful when you want a second opinion on a whole repo. CX Codex +1 4,000installs reducing-entropy A skill that measures success by the size of the codebase afterwards, not by the effort spent. It runs only when you ask, and it deletes. CX Codex +1 4,000installs plugin-forge Skill for creating and managing Claude Code plugins with proper structure, manifests, and marketplace integration. Use it when building plugins for distribution or setting up plugin components like commands and hooks. CX Codex +1 3,900installs

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor