React Code Review Checklist
Rapid-scan checklist for React web application code reviews. For React Native, see React Native Code Review Checklist.
Security Red Flags - Block PR Immediately
- dangerouslySetInnerHTML without sanitization: HTML injection vulnerability → React Security Best Practices
- API keys in source code: Exposed credentials in component files, constants, or environment files committed to repo
- Credentials in localStorage: Tokens, passwords stored in localStorage instead of secure httpOnly cookies
- eval() or Function() constructor: Code execution vulnerabilities
- Unvalidated redirects:
window.locationornavigate()with user input without validation - XSS in user-generated content: Rendering user input without escaping (e.g., in innerHTML, dangerouslySetInnerHTML)
- Exposed sensitive data in client state: PII, full credit card numbers, passwords in Redux/Zustand state
- Missing CSRF protection: State-changing operations without CSRF tokens
- Insecure dependencies: Known vulnerabilities in npm packages (check with
npm audit)
Common Mistakes
Hooks Usage
- Missing dependencies in
useEffect/useCallback/useMemodependency arrays → React General - Hooks - Stale closures capturing old state/props values
- Calling hooks conditionally or inside loops (violates Rules of Hooks)
useStateinside loops or conditional blocks- Not using
useCallbackfor event handlers passed as props to memoized children - Forgetting cleanup functions in
useEffect(subscriptions, timers, listeners)
Re-render Issues
- Infinite re-render loops (state update triggering effect that updates state) → React Performance - Re-render Optimization
- Creating new object/array literals in render (
{},[]) passed as props - Defining components inside other components (causes remounting)
- Anonymous functions as props without
useCallbackto memoized components
Component Design
- Prop drilling through 3+ levels instead of context or composition → React General - Component Composition
- Components larger than 200-300 lines (extract sub-components)
- Business logic mixed with rendering logic (extract to custom hooks)
- Using class components instead of functional components with hooks
State Management
- Overusing global state (Zustand/Redux) for local UI state → React State Management
- Mutating state directly instead of creating new objects/arrays
- Not using
immeror spread operators for nested state updates - Storing derived data in state instead of computing from existing state
- Missing error state and loading state in async operations
TypeScript Issues
- Using
anytype instead of proper types - Missing prop type definitions on components
- Not typing event handlers (
React.MouseEvent,React.ChangeEvent) - Inline type definitions instead of reusable interfaces/types
Watch For - Performance & Correctness Issues
Performance
- Missing
React.memoon expensive pure components → React Performance - Memoization - Missing
useMemofor expensive calculations - Large component trees without code splitting (
React.lazy, dynamic imports) - Unoptimized images (missing lazy loading, wrong formats, no responsive images)
- Not virtualizing long lists (use
react-windoworreact-virtual) → React Performance - Re-rendering entire lists when only one item changes (missing proper
keyprop)
Memory Leaks
- Missing cleanup in
useEffectfor event listeners - Subscriptions not unsubscribed on unmount
- Timers (
setTimeout,setInterval) not cleared - Async operations updating state after component unmounts
Error Handling
- Missing error boundaries around feature modules → React General - Error Boundaries
- No error state in async data fetching
- Unhandled promise rejections
- Using
console.errorinstead of proper error reporting service
Forms
- Not using controlled components for form inputs → React Forms
- Missing form validation before submission
- No loading/disabled state during form submission
- Storing sensitive form data in unencrypted state
Data Fetching
- Fetching data in components instead of custom hooks or query library → React General - Data Fetching
- Not handling loading and error states
- No request deduplication for identical concurrent requests
- Missing request cancellation on component unmount
- Refetching data unnecessarily on every render
Best Practices - Code Quality
Component Structure
- Functional components with TypeScript → React General
- Props interface defined above component
- Destructured props in function signature
- Early returns for conditional rendering
- Extract complex JSX into sub-components or variables
- Use composition over prop drilling
Hooks Best Practices
- Custom hooks for reusable stateful logic → React General - Custom Hooks
- Prefix custom hooks with
use(naming convention) - All dependencies included in hook dependency arrays
- Cleanup functions for subscriptions, timers, listeners
useCallbackfor functions passed to memoized child componentsuseMemofor expensive computations
State Management
- Local state (
useState) for UI-only state → React State Management - Context for theme, user preferences, rarely-changing data
- Zustand/Redux for complex shared state across features
- React Query for server state (data fetching, caching)
- Immutable state updates (spread operators or
immer)
Performance
React.memofor expensive pure components → React Performance- Code splitting with
React.lazyandSuspense - Lazy load images with
loading="lazy" - Virtualized lists for 100+ items
- Debounce/throttle expensive operations (search, scroll handlers)
- Web Vitals monitoring (LCP, FID, CLS)
Testing
- Unit tests for custom hooks and utility functions → React Testing
- Integration tests for component interactions
- Accessibility tests (axe, jest-axe)
- Test user interactions, not implementation details
- Mock external dependencies (API calls, timers)
Accessibility
- Semantic HTML elements (
<button>,<nav>,<main>) - ARIA labels where semantic HTML insufficient
- Keyboard navigation support (focus management, tab order)
- Color contrast meeting WCAG 2.1 AA standards (4.5:1 for text)
- Focus indicators visible on interactive elements
- Screen reader testing (NVDA, VoiceOver)
Quick Scan Table
| Category | What to Check | Severity | Reference |
|---|---|---|---|
| Security | No dangerouslySetInnerHTML without sanitization | Block | Security |
| Security | No API keys in source code | Block | Configuration |
| Security | No credentials in localStorage | Block | Security |
| Hooks | Dependencies complete in useEffect/useCallback/useMemo | Fix | Hooks |
| Hooks | Cleanup functions for subscriptions/timers | Fix | Hooks |
| Hooks | No hooks in conditionals/loops | Block | Hooks |
| Performance | No infinite re-render loops | Fix | Performance |
| Performance | Long lists virtualized | Fix | Performance |
| GOOD: | Performance | Code splitting for routes | Review |
| State | No direct state mutation | Fix | State Management |
| GOOD: | State | Proper state location (local vs global) | Review |
| TypeScript | No any types | Fix | React General |
| TypeScript | Props interfaces defined | Fix | React General |
| GOOD: | Accessibility | Semantic HTML used | Review |
| Accessibility | Keyboard navigation works | Fix | Accessibility |
| Error Handling | Error boundaries present | Fix | React General |
| GOOD: | Testing | Critical paths tested | Review |
Legend:
- Block PR - Security vulnerability or critical bug
- Fix Before Merge - Performance issue, correctness problem, or likely bug
- [GOOD] Review & Discuss - Code quality improvement
Additional Considerations
Component Anti-Patterns
- God components (500+ lines, too many responsibilities)
- Premature optimization (memoizing everything)
- Overusing Context (causes unnecessary re-renders)
- Not splitting code by route
- Inconsistent file/folder naming
- Missing TypeScript strict mode configuration
Styling Issues
- Inline styles for static styles (use CSS modules or styled-components)
- Hardcoded colors instead of theme variables
- Missing responsive design (mobile, tablet, desktop)
- No dark mode support when required
- Accessibility issues (color contrast, focus indicators)
Build & Bundle
- Bundle size over 500KB (before compression) for initial load
- No tree shaking configured
- Development dependencies in production bundle
- Missing source maps for production debugging
- No asset optimization (images, fonts)
Modern React Features
- Not using React 18 concurrent features when beneficial (startTransition, useDeferredValue)
- Missing Suspense boundaries for code splitting
- Not leveraging server components (if using Next.js 13+)
Block Checklist - Must Fix Before Merge
- No dangerouslySetInnerHTML without sanitization
- No API keys or secrets in source code
- No credentials in localStorage
- Hooks not called conditionally or in loops
- All useEffect dependencies included or intentionally omitted
- Cleanup functions present for subscriptions/timers
- No infinite re-render loops
- No direct state mutation
- TypeScript errors resolved (no
@ts-ignorewithout explanation) - Error boundaries present for feature modules
Related Documentation
React-Specific Guides:
- React General Guidelines - Components, hooks, patterns
- React State Management - Zustand, Context, local state
- React Performance - Memoization, code splitting, optimization
- React Testing - Testing Library, hooks testing, mocking
- React Forms - Form handling, validation
Cross-Platform:
- React Native Code Review Checklist - Mobile-specific checklist
- TypeScript Guidelines
- Frontend Best Practices
- Accessibility Guidelines
Process: