[ PROMPT_NODE_25267 ]
React Dev
[ SKILL_DOCUMENTATION ]
# React TypeScript
Type-safe React = compile-time guarantees = confident refactoring.
- 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
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;
} & React.ComponentPropsWithoutRef;
function Button({ ref, children, ...props }: ButtonProps) {
return ;
}
```
**useActionState** - replaces useFormState:
```typescript
import { useActionState } from 'react';
type FormState = { errors?: string[]; success?: boolean };
function Form() {
const [state, formAction, isPending] = useActionState(submitAction, {});
return ...;
}
```
**use()** - unwraps promises/context:
```typescript
function UserProfile({ userPromise }: { userPromise: Promise }) {
const user = use(userPromise); // Suspends until resolved
return
);
}
```
**Constrained generics** for required properties:
```typescript
type HasId = { id: string | number };
function List({ items }: { items: T[] }) {
return
{user.name}
;
}
```
See [react-19-patterns.md](references/react-19-patterns.md) for useOptimistic, useTransition, migration checklist.
**Props** - extend native elements:
```typescript
type ButtonProps = {
variant: 'primary' | 'secondary';
} & React.ComponentPropsWithoutRef;
function Button({ variant, children, ...props }: ButtonProps) {
return ;
}
```
**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 Link;
}
return ;
}
```
Use specific event types for accurate target typing:
```typescript
// Mouse
function handleClick(e: React.MouseEvent) {
e.currentTarget.disabled = true;
}
// Form
function handleSubmit(e: React.FormEvent) {
e.preventDefault();
const formData = new FormData(e.currentTarget);
}
// Input
function handleChange(e: React.ChangeEvent) {
console.log(e.target.value);
}
// Keyboard
function handleKeyDown(e: React.KeyboardEvent) {
if (e.key === 'Enter') e.currentTarget.blur();
}
```
See [event-handlers.md](references/event-handlers.md) for focus, drag, clipboard, touch, wheel events.
**useState** - explicit for unions/null:
```typescript
const [user, setUser] = useState(null);
const [status, setStatus] = useState('idle');
```
**useRef** - null for DOM, value for mutable:
```typescript
const inputRef = useRef(null); // DOM - use ?.
const countRef = useRef(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(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.
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 = {
key: keyof T;
header: string;
render?: (value: T[keyof T], item: T) => React.ReactNode;
};
type TableProps = {
data: T[];
columns: Column[];
keyExtractor: (item: T) => string | number;
};
function Table({ data, columns, keyExtractor }: TableProps) {
return (
| {col.header} | )}
|---|
| {col.render ? col.render(item[col.key], item) : String(item[col.key])} | ))}
- {items.map(item =>
- ... )}
{user.name}
;
}
```
**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 ...;
}
```
**use() for promise handoff**:
```typescript
// Server: pass promise without await
async function Page() {
const userPromise = fetchUser('123');
return ;
}
// Client: unwrap with use()
'use client';
function UserProfile({ userPromise }: { userPromise: Promise }) {
const user = use(userPromise);
return {user.name}
;
}
```
See [server-components.md](examples/server-components.md) for parallel fetching, streaming, error boundaries.
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 {user.name}
; } ``` See [tanstack-router.md](references/tanstack-router.md) for TanStack patterns and [react-router.md](references/react-router.md) for React Router patterns. 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() - [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
Source: claude-code-templates (MIT). See About Us for full credits.