All skills
softaworks avatar

/react-dev

@1c0662a
by softaworkssoftaworks/agent-toolkit2.5k stars
230

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).

Use this Skill: https://skilld.dev/gh/softaworks/agent-toolkit/react-dev

This session only. Nothing lands on disk.

SKILL.md

≈84 tokens always: the name and description. ≈2.5k when used: this file. ≈28k more on demand in 8 files.

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:

// 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:

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:

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

See react-19-patterns.md for useOptimistic, useTransition, migration checklist.

</react_19_changes><component_patterns>

Props - extend native elements:

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

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

Children typing:

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

Discriminated unions for variant props:

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:

// 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 for focus, drag, clipboard, touch, wheel events.

</event_handlers><hooks_typing>

useState - explicit for unions/null:

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

useRef - null for DOM, value for mutable:

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

useReducer - discriminated unions for actions:

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:

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

useContext - null guard pattern:

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 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:

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:

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 for Select, List, Modal, FormField patterns.

</generic_components><server_components>

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

Async data fetching:

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:

'use server';

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

Client + Server Action:

'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:

// 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 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:

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:

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 for TanStack patterns and 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> </references>

Source: SKILL.md on GitHub

No alerts16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The react-dev skill is a comprehensive documentation and reference guide for building React applications with TypeScript, covering React 18 and 19 features, hooks, and routing. No security issues were detected.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer7mo

    9 files scanned · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 1c0662a. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Dormantupdated 8 months ago
version
1.0.0
  • React
  • TypeScript
  • react-19
  • hooks
  • server-components
  • tanstack-router
  • react-router
  • event-handlers
  • generic-components

README badge

README badge for softaworks/agent-toolkit/react-dev

Teaches type-safe React component patterns with TypeScript across React 18-19, including generic components, event handler typing, hooks, Server Components, and router integration (TanStack Router, React Router v7). Covers React 19 breaking changes like ref-as-prop, useActionState, and use() for promise unwrapping.

Generated from the current SKILL.md.

Does this skill cover React 19?
Yes. The skill covers React 19 breaking changes including ref as prop (forwardRef deprecated), useActionState (replaces useFormState), and use() for unwrapping promises and context.
What routing libraries does this support?
TanStack Router and React Router v7. Both support type-safe routing with compile-time validation of params and search query strings.
Does this cover Server Components and Server Actions?
Yes. The skill includes patterns for async Server Components, 'use server' Server Actions, and passing promises to Client Components via use().
Can I use this with generic components?
Yes. The skill covers generic component patterns including constrained generics, keyof T for type-safe column access, and render props for custom rendering.
Does this work with non-TypeScript React?
No. The skill requires TypeScript and focuses exclusively on type-safe patterns for React 18-19.

Generated from the current SKILL.md. These answers refresh after source changes.