name: review-react description: Reviews React and TypeScript code for correctness, security, hook usage, and multi-tenancy safety. Use when reviewing generated React components, hooks, or TypeScript files, or when asked to do a code review on frontend code.
React Code Review
Targets React 19 with the React Compiler. Work through the five areas in order. For the full pass/fail checklist, see checklist.md.
1. Component patterns
- Functional components only — no class components
- One clear responsibility per component; extract if JSX exceeds ~100 lines
- No business logic or data-fetching directly inside JSX
- Props typed with explicit interfaces — no
any, no implicit{} - No component defined inside another component body
- List keys must be stable and unique (never array index for dynamic lists)
2. Hooks
Core rules (non-negotiable)
- Never call hooks conditionally or inside loops — except
use(), which is designed for conditional calls - Enable
react-hooks/rules-of-hooksandreact-hooks/exhaustive-depsESLint rules; treat all warnings as errors
React 19 hook additions to know
| Hook | Use for |
|---|---|
use(promise|context) | Reading context or async values; can be called conditionally |
useActionState | Form submission state + pending flag — replaces manual useState + loading patterns |
useFormStatus | Child components reading parent form submission state |
useOptimistic | Optimistic UI updates before server confirmation |
Effects
- Effects are for syncing with external systems only — not for pure computations
useEffectdeps array must be exhaustive; missing deps hide stale closures- Fetch cleanup must use
AbortController, not just amountedflag:
useEffect(() => {
const ac = new AbortController();
fetch(`/api/notes/${id}`, { signal: ac.signal })
.then((r) => r.json())
.then(setNote)
.catch((e) => {
if (e.name !== "AbortError") setError(e);
});
return () => ac.abort();
}, [id]);
useLayoutEffectonly for DOM measurements or synchronizing layout — never as a first choice- Stale values that must not re-trigger effects → store in a
useRef, readref.currentinside the effect
3. Performance (React 19 compiler-aware)
The React Compiler automatically memoizes at build time. Do not add useMemo / useCallback / React.memo by default.
Add manual memoization only in these three cases:
- Third-party libraries that require stable references
- Effects with function dependencies that would cause infinite loops without stabilization
- Genuinely expensive computations with external / non-React data
Use useTransition to keep urgent interactions (typing, gestures) responsive while deferring heavy renders.
Use useDeferredValue to defer a derived value to a slower render.
4. Security
dangerouslySetInnerHTMLwith any user-supplied string → critical bug; must sanitize with DOMPurify firsthref={userInput}→ critical bug; validate URL starts withhttps://or use an allowlist- Auth tokens stored in
localStorage→ critical bug; must usehttpOnlycookies - No secrets, API keys, or internal IDs hardcoded in client source
- No sensitive data logged to
consolein production paths - Content Security Policy headers enforced at the infrastructure/server level
5. Multi-tenancy safety
orgId/tenantIdused in API calls must come from the server-side session (token/cookie), never from URL params or component state alone- Switching orgs must destroy and re-initialize the entire tenant context, not just invalidate queries
- Access checks happen on the API — UI hiding (conditional rendering, CSS) is UX only, never security
- File/note access must not rely on the client passing its own permission level
Additional resources
- Full pass/fail line-item checklist → checklist.md