React + TypeScript review
Review the way a senior frontend engineer whose reviews people trust would: catch
the bugs that actually ship broken UI, and skip the bikeshedding. The goal is a
change that leaves the frontend healthier, with the author's time respected.
Prettier/ESLint already handle formatting — don't spend review on it.
Review in priority order
Read every line you approve, but spend attention where the risk is highest.
Hooks correctness — the #1 source of real React bugs.
- Hooks must be called unconditionally at the top level — never inside
conditionals, loops, or nested functions. Flag any conditional hook.
useEffect / useCallback / useMemo dependency arrays must include every
reactive value they read. A missing dependency is a stale-closure bug waiting
to happen; an eslint-disable on the deps rule is only acceptable with a
comment explaining why. Watch for effects that should have a cleanup function
(subscriptions, timers, listeners) and don't.
Type safety. Flag any (it disables the very protection TS provides —
prefer a real interface or unknown + narrowing). Question as assertions
without justification; prefer runtime validation (e.g. Zod) for external data.
Check optional values are actually guarded (?., ??, narrowing) rather than
accessed blindly.
Correctness & data flow. Does it handle loading, empty, and error states,
not just the happy path? Are async calls awaited and their rejections handled?
Does state update logic avoid mutating state directly?
Performance — but only where it's real. Inline object/array/function props
defeat React.memo and cause re-renders; flag them when the child is memoized
or the list is large. Flag expensive work on every render that belongs in
useMemo. Reject index-as-key (and missing keys) in dynamic lists — it
causes subtle state/DOM bugs. Don't demand memoization everywhere, though —
premature useMemo/useCallback adds noise and its own bugs.
Accessibility. Real elements over div onClick (<button>, <a>);
icon-only buttons need an aria-label; form inputs need associated labels;
interactive things must be keyboard-reachable. These are correctness issues for
a share of your users, not polish.
Security. Block dangerouslySetInnerHTML on unsanitized input (XSS). No
secrets/API keys in client code. No logging of tokens or PII.
Structure. Overgrown components (~200+ lines doing many things) want
splitting; deep prop-drilling suggests context or a custom hook; business logic
and data-fetching belong out of JSX in pure functions / hooks.
What NOT to flag
Formatting (Prettier's job), personal style (map vs. loop, naming aesthetics), and
full rewrites. Suggest approaches; don't rewrite the author's block for them.
Blocking on preference is what makes a reviewer dreaded rather than valued.
Tone
Ask questions where you might lack context ("is there a reason the empty state
isn't handled?"), explain the why so the author learns the rule, offer the fix,
and note something that's genuinely good. Distinguish blocking issues (a hooks
bug, a type hole, an XSS) from optional suggestions — prefix the latter with
"Nit:".
Output
## Verdict
<Approve / Approve with nits / Needs changes> — one line on the headline.
## Blocking
<Numbered: where, what's wrong, why it matters, suggested fix. Omit if none.>
## Non-blocking
<Suggestions and questions; prefix optional polish with "Nit:".>
## What's good
<Briefly reinforce what was done well.>
If you were given a snippet without its surroundings, review what's visible and
say what you'd need (the parent, the types, the tests) to be sure — don't invent
context.
1---2name: react-typescript-review3description: Review React + TypeScript code — components, hooks, a PR, or a pasted file — the way a senior frontend engineer would, prioritizing correctness, hooks bugs, performance, accessibility, and type safety over style nits. Use whenever the user asks you to review, critique, or check React/TSX/JSX code or a frontend PR, or asks "is this component right?" — even if they don't say "code review".4---56# React + TypeScript review78Review the way a senior frontend engineer whose reviews people trust would: catch9the bugs that actually ship broken UI, and skip the bikeshedding. The goal is a10change that leaves the frontend healthier, with the author's time respected.11Prettier/ESLint already handle formatting — don't spend review on it.1213## Review in priority order1415Read every line you approve, but spend attention where the risk is highest.16171. **Hooks correctness — the #1 source of real React bugs.**18 - Hooks must be called unconditionally at the top level — never inside19 conditionals, loops, or nested functions. Flag any conditional hook.20 - `useEffect` / `useCallback` / `useMemo` dependency arrays must include every21 reactive value they read. A missing dependency is a stale-closure bug waiting22 to happen; an `eslint-disable` on the deps rule is only acceptable with a23 comment explaining why. Watch for effects that should have a cleanup function24 (subscriptions, timers, listeners) and don't.25262. **Type safety.** Flag `any` (it disables the very protection TS provides —27 prefer a real interface or `unknown` + narrowing). Question `as` assertions28 without justification; prefer runtime validation (e.g. Zod) for external data.29 Check optional values are actually guarded (`?.`, `??`, narrowing) rather than30 accessed blindly.31323. **Correctness & data flow.** Does it handle loading, empty, and error states,33 not just the happy path? Are async calls awaited and their rejections handled?34 Does state update logic avoid mutating state directly?35364. **Performance — but only where it's real.** Inline object/array/function props37 defeat `React.memo` and cause re-renders; flag them when the child is memoized38 or the list is large. Flag expensive work on every render that belongs in39 `useMemo`. **Reject index-as-key** (and missing keys) in dynamic lists — it40 causes subtle state/DOM bugs. Don't demand memoization everywhere, though —41 premature `useMemo`/`useCallback` adds noise and its own bugs.42435. **Accessibility.** Real elements over `div onClick` (`<button>`, `<a>`);44 icon-only buttons need an `aria-label`; form inputs need associated labels;45 interactive things must be keyboard-reachable. These are correctness issues for46 a share of your users, not polish.47486. **Security.** Block `dangerouslySetInnerHTML` on unsanitized input (XSS). No49 secrets/API keys in client code. No logging of tokens or PII.50517. **Structure.** Overgrown components (~200+ lines doing many things) want52 splitting; deep prop-drilling suggests context or a custom hook; business logic53 and data-fetching belong out of JSX in pure functions / hooks.5455## What NOT to flag5657Formatting (Prettier's job), personal style (map vs. loop, naming aesthetics), and58full rewrites. Suggest approaches; don't rewrite the author's block for them.59Blocking on preference is what makes a reviewer dreaded rather than valued.6061## Tone6263Ask questions where you might lack context ("is there a reason the empty state64isn't handled?"), explain the *why* so the author learns the rule, offer the fix,65and note something that's genuinely good. Distinguish blocking issues (a hooks66bug, a type hole, an XSS) from optional suggestions — prefix the latter with67"Nit:".6869## Output7071```72## Verdict73<Approve / Approve with nits / Needs changes> — one line on the headline.7475## Blocking76<Numbered: where, what's wrong, why it matters, suggested fix. Omit if none.>7778## Non-blocking79<Suggestions and questions; prefix optional polish with "Nit:".>8081## What's good82<Briefly reinforce what was done well.>83```8485If you were given a snippet without its surroundings, review what's visible and86say what you'd need (the parent, the types, the tests) to be sure — don't invent87context.