typescript-react-reviewer

Expert code reviewer for TypeScript and React 19 applications with deep anti-pattern detection. Identifies critical issues including useEffect abuse, state mutations, conditional hook calls, and React 19-specific bugs like useFormStatus in form components Covers three priority levels: critical (blocks merge), high priority (stale closures, missing boundaries), and architecture/style recommendations Includes state management guidance for server data (TanStack Query), global UI state (Zustand/Jotai), and form state (React 19 useActionState) Provides TypeScript safety checks with recommended compiler options including strict , noUncheckedIndexedAccess , and exactOptionalPropertyTypes

INSTALLATION
npx skills add https://github.com/dotneet/claude-code-marketplace --skill typescript-react-reviewer
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

TypeScript + React 19 Code Review Expert

Expert code reviewer with deep knowledge of React 19's new features, TypeScript best practices, state management patterns, and common anti-patterns.

Review Priority Levels

🚫 Critical (Block Merge)

These issues cause bugs, memory leaks, or architectural problems:

Issue

Why It's Critical

useEffect for derived state

Extra render cycle, sync bugs

Missing cleanup in useEffect

Memory leaks

Direct state mutation (.push(), .splice())

Silent update failures

Conditional hook calls

Breaks Rules of Hooks

key={index} in dynamic lists

State corruption on reorder

any type without justification

Type safety bypass

useFormStatus in same component as <form>

Always returns false (React 19 bug)

Promise created inside render with use()

Infinite loop

⚠️ High Priority

Issue

Impact

Incomplete dependency arrays

Stale closures, missing updates

Props typed as any

Runtime errors

Unjustified useMemo/useCallback

Unnecessary complexity

Missing Error Boundaries

Poor error UX

Controlled input initialized with undefined

React warning

πŸ“ Architecture/Style

Issue

Recommendation

Component > 300 lines

Split into smaller components

Prop drilling > 2-3 levels

Use composition or context

State far from usage

Colocate state

Custom hooks without use prefix

Follow naming convention

Quick Detection Patterns

useEffect Abuse (Most Common Anti-Pattern)

// ❌ WRONG: Derived state in useEffect

const [firstName, setFirstName] = useState('');

const [fullName, setFullName] = useState('');

useEffect(() => {

  setFullName(firstName + ' ' + lastName);

}, [firstName, lastName]);

// βœ… CORRECT: Compute during render

const fullName = firstName + ' ' + lastName;
// ❌ WRONG: Event logic in useEffect

useEffect(() => {

  if (product.isInCart) showNotification('Added!');

}, [product]);

// βœ… CORRECT: Logic in event handler

function handleAddToCart() {

  addToCart(product);

  showNotification('Added!');

}

React 19 Hook Mistakes

// ❌ WRONG: useFormStatus in form component (always returns false)

function Form() {

  const { pending } = useFormStatus();

  return <form action={submit}><button disabled={pending}>Send</button></form>;

}

// βœ… CORRECT: useFormStatus in child component

function SubmitButton() {

  const { pending } = useFormStatus();

  return <button type="submit" disabled={pending}>Send</button>;

}

function Form() {

  return <form action={submit}><SubmitButton /></form>;

}
// ❌ WRONG: Promise created in render (infinite loop)

function Component() {

  const data = use(fetch('/api/data')); // New promise every render!

}

// βœ… CORRECT: Promise from props or state

function Component({ dataPromise }: { dataPromise: Promise<Data> }) {

  const data = use(dataPromise);

}

State Mutation Detection

// ❌ WRONG: Mutations (no re-render)

items.push(newItem);

setItems(items);

arr[i] = newValue;

setArr(arr);

// βœ… CORRECT: Immutable updates

setItems([...items, newItem]);

setArr(arr.map((x, idx) => idx === i ? newValue : x));

TypeScript Red Flags

// ❌ Red flags to catch

const data: any = response;           // Unsafe any

const items = arr[10];                // Missing undefined check

const App: React.FC<Props> = () => {}; // Discouraged pattern

// βœ… Preferred patterns

const data: ResponseType = response;

const items = arr[10]; // with noUncheckedIndexedAccess

const App = ({ prop }: Props) => {};  // Explicit props

Review Workflow

  • Scan for critical issues first - Check for the patterns in "Critical (Block Merge)" section
  • Evaluate state management - Is state colocated? Server state vs client state separation?
  • Assess TypeScript safety - Generic components, discriminated unions, strict config
  • Review for maintainability - Component size, hook design, folder structure

Reference Documents

For detailed patterns and examples:

  • react19-patterns.md - React 19 new hooks (useActionState, useOptimistic, use), Server/Client Component boundaries
  • checklist.md - Full code review checklist for thorough reviews

State Management Quick Guide

Data Type

Solution

Server/async data

TanStack Query (never copy to local state)

Simple global UI state

Zustand (~1KB, no Provider)

Fine-grained derived state

Jotai (~2.4KB)

Component-local state

useState/useReducer

Form state

React 19 useActionState

TanStack Query Anti-Pattern

// ❌ NEVER copy server data to local state

const { data } = useQuery({ queryKey: ['todos'], queryFn: fetchTodos });

const [todos, setTodos] = useState([]);

useEffect(() => setTodos(data), [data]);

// βœ… Query IS the source of truth

const { data: todos } = useQuery({ queryKey: ['todos'], queryFn: fetchTodos });

TypeScript Config Recommendations

{

  "compilerOptions": {

    "strict": true,

    "noUncheckedIndexedAccess": true,

    "noImplicitReturns": true,

    "exactOptionalPropertyTypes": true

  }

}

noUncheckedIndexedAccess is critical - it catches arr[i] returning undefined.

Immediate Red Flags

When reviewing, flag these immediately:

Pattern

Problem

Fix

eslint-disable react-hooks/exhaustive-deps

Hides stale closure bugs

Refactor logic

Component defined inside component

Remounts every render

Move outside

useState(undefined) for inputs

Uncontrolled warning

Use empty string

React.FC with generics

Generic inference breaks

Use explicit props

Barrel files (index.ts) in app code

Bundle bloat, circular deps

Direct imports

BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills β†’

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free Β· no credit card