# React Expert

> Writes performant React code and refactors inefficient patterns. Use whenever editing or creating .jsx or .tsx files, implementing React components, optimizing slow UIs, or fixing re-render issues.

- Skill: `maxwellcohen/react-expert` (Agent Skill)
- Install (CLI): `npx skillmds@latest add maxwellcohen/react-expert`
- Raw SKILL.md: https://api.skillmd.com/api/skills/maxwellcohen/react-expert/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: MaxwellCohen (https://skillmd.com/u/maxwellcohen)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/maxwellcohen/react-expert

---


# React Expert

Write fast React code and fix inefficient patterns. Profile first; optimize only where it matters.

## Profile First

- Do not add `memo` / `useCallback` / `useMemo` preemptively.
- Use React DevTools Profiler to identify actual bottlenecks.
- React 19 compiler auto-optimizes; manual hooks still needed for third-party libs, effect deps, and expensive external-data computations.

## Writing Fast Code (Prevention)

- **Avoid object/array creation in render** — Inline `{}` or `[]` as props/context creates new references every render, breaking memo and causing child re-renders.
- **Stable callbacks** — Use `useCallback` when passing handlers to memoized children or to effects; otherwise prefer inline functions for simplicity.
- **Expensive computations** — Use `useMemo` only for costly derivations (filtering/sorting large lists, heavy transforms).
- **Context** — Split contexts by update frequency; avoid putting frequently-changing values in a single context.
- **Code splitting** — Use `React.lazy` + `Suspense` for route-level or heavy below-the-fold components.
- **Lists** — Virtualize long lists (react-window, @tanstack/react-virtual) when rendering 100+ items.
- **Async React** — Use `useTransition`, `useOptimistic`, and `useActionState` for non-blocking updates and optimistic UI.

## Async React (useTransition, useOptimistic, useActionState)

- **useTransition** — `[isPending, startTransition]`. Wrap state updates and async work in `startTransition` to keep the UI responsive. Transitions are non-blocking and can be interrupted (e.g. user clicks another tab). Use `isPending` for loading feedback. Caveat: state updates after `await` must be wrapped in another `startTransition` (current limitation).
- **useOptimistic** — `[optimisticState, setOptimistic]`. Show instant UI feedback while an Action runs; React reverts when the Action completes. Call `setOptimistic` only inside an Action (inside `startTransition`); otherwise React warns and the optimistic state briefly flashes. Pairs with `useTransition` for async actions.
- **useActionState** — `[state, dispatchAction, isPending]`. Manages state from async Actions (e.g. form submissions). The reducer action can be async and perform side effects. Call `dispatchAction` only from an Action (`startTransition` or action prop). Good for forms, mutations, and progressive enhancement with Server Functions.

**Pattern:** Use `startTransition` for the async wrapper, `useOptimistic` for instant feedback, and `useActionState` when you need action-derived state (e.g. form errors, mutation result).

## Fixing Inefficient Code (Optimization)

- **Re-render cascades** — Trace from parent; fix by memoizing children, stabilizing props, or splitting context.
- **Object/array in props** — Extract to `useMemo` or move outside component.
- **Inline functions as props** — Wrap in `useCallback` when child is memoized and re-renders are costly.
- **Heavy work in render** — Move to `useMemo` or `useEffect` + state.
- **Large lists** — Add virtualization; avoid mapping over thousands of items.

## Patterns to Avoid

- `memo` on every component (adds overhead; use only where profiling shows benefit).
- Creating components inside other components (new reference each render).
- Spreading `...props` into memoized children when props include unstable values.
- Single giant context for entire app state.

## Examples

**Inline object in props (before → after):**

```tsx
// Before: new object every render, breaks memo
<ExpensiveChild style={{ margin: 8 }} />

// After: stable reference
const style = useMemo(() => ({ margin: 8 }), []);
<ExpensiveChild style={style} />
```

**Context split (before → after):**

```tsx
// Before: theme + user both in one context; theme changes re-render user consumers
const AppContext = createContext({ theme: 'light', user: null });

// After: split by update frequency
const ThemeContext = createContext('light');
const UserContext = createContext(null);
```

**Async with useTransition + useOptimistic:**

```tsx
function LikeButton({ isLiked, onLike }) {
  const [isPending, startTransition] = useTransition();
  const [optimisticLiked, setOptimisticLiked] = useOptimistic(isLiked);

  function handleClick() {
    startTransition(async () => {
      setOptimisticLiked(!optimisticLiked);
      await onLike();
    });
  }

  return (
    <button onClick={handleClick} disabled={isPending}>
      {optimisticLiked ? 'Liked' : 'Like'}
    </button>
  );
}
```

## Checklist

- [ ] Profile before optimizing
- [ ] No inline objects/arrays as props to memoized children
- [ ] Callbacks passed to memoized children wrapped in `useCallback` when needed
- [ ] Expensive derivations in `useMemo`
- [ ] Long lists virtualized
- [ ] Heavy/route-level components lazy-loaded
- [ ] Context split by update frequency
- [ ] Async actions use `useTransition`; optimistic UI uses `useOptimistic`; form/action state uses `useActionState`

