React Code Review
Overview
This skill provides structured, comprehensive code review for React applications. It evaluates code against React 19 best practices, component architecture patterns, hook usage, accessibility standards, and production-readiness criteria. The review produces actionable findings categorized by severity (Critical, Warning, Suggestion) with concrete code examples for improvements.
This skill delegates to the react-software-architect-review agent for deep architectural analysis when invoked through the agent system.
When to Use
- Reviewing React components, hooks, and pages before merging
- Validating component composition and reusability patterns
- Checking proper hook usage (useState, useEffect, useMemo, useCallback)
- Reviewing React 19 patterns (use, useOptimistic, useFormStatus, Actions)
- Evaluating state management approaches (local, context, external stores)
- Assessing performance optimization (memoization, code splitting, lazy loading)
- Reviewing accessibility compliance (WCAG, semantic HTML, ARIA)
- Validating TypeScript typing for props, state, and events
- Checking Tailwind CSS and styling patterns
- After implementing new React features or refactoring component architecture
Instructions
Identify Scope: Determine which React components and hooks are under review. Use glob to discover .tsx/.jsx files and grep to identify component definitions, hook usage, and context providers.
Analyze Component Architecture: Verify proper component composition — check for single responsibility, appropriate size, and reusability. Look for components that are too large (>200 lines), have too many props (>7), or mix concerns.
Review Hook Usage: Validate proper hook usage — check dependency arrays in useEffect/useMemo/useCallback, verify cleanup functions in useEffect, and identify unnecessary re-renders caused by missing or incorrect memoization.
Evaluate State Management: Assess where state lives — check for proper colocation, unnecessary lifting, and appropriate use of Context vs external stores. Verify that server state uses TanStack Query, SWR, or similar libraries rather than manual useEffect + useState patterns.
Check Accessibility: Review semantic HTML usage, ARIA attributes, keyboard navigation, focus management, and screen reader compatibility. Verify that interactive elements are accessible and form inputs have proper labels.
Assess Performance: Look for unnecessary re-renders, missing React.memo on expensive components, improper use of useCallback/useMemo, missing code splitting, and large bundle imports.
Review TypeScript Integration: Check prop type definitions, event handler typing, generic component patterns, and proper use of utility types. Verify that any is not used where specific types are possible.
Produce Review Report: Generate a structured report with severity-classified findings (Critical, Warning, Suggestion), positive observations, and prioritized recommendations with code examples.
Examples
Example 1: Hook Dependency Issues
// ❌ Bad: Missing dependency causes stale closure
function UserProfile({ userId }: { userId: string }) {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
fetchUser(userId).then(setUser);
}, []); // Missing userId in dependency array
return <div>{user?.name}</div>;
}
// ✅ Good: Proper dependencies with cleanup
function UserProfile({ userId }: { userId: string }) {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
let cancelled = false;
fetchUser(userId).then((data) => {
if (!cancelled) setUser(data);
});
return () => { cancelled = true; };
}, [userId]);
return <div>{user?.name}</div>;
}
// ✅ Better: Use TanStack Query for server state
function UserProfile({ userId }: { userId: string }) {
const { data: user, isLoading } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
});
if (isLoading) return <Skeleton />;
return <div>{user?.name}</div>;
}
Example 2: Component Composition
// ❌ Bad: Monolithic component mixing data fetching, filtering, and rendering
function Dashboard() {
const [users, setUsers] = useState([]);
const [filter, setFilter] = useState('');
useEffect(() => { /* fetch + filter + sort all in one */ }, [filter]);
return <div>{/* 200+ lines of mixed concerns */}</div>;
}
// ✅ Good: Composed from focused components with custom hooks
function Dashboard() {
return (
<div>
<UserFilters />
<Suspense fallback={<TableSkeleton />}>
<UserTable />
</Suspense>
<UserPagination />
</div>
);
}
Example 3: Accessibility Review
// ❌ Bad: Inaccessible interactive elements
function Menu({ items }: { items: MenuItem[] }) {
const [open, setOpen] = useState(false);
return (
<div>
<div => setOpen(!open)}>Menu</div>
{open && (
<div>
{items.map(item => (
<div key={item.id} => navigate(item.path)}>
{item.label}
</div>
))}
</div>
)}
</div>
);
}
// ✅ Good: Accessible with proper semantics and keyboard support
function Menu({ items }: { items: MenuItem[] }) {
const [open, setOpen] = useState(false);
return (
<nav aria-label="Main navigation">
<button
=> setOpen(!open)}
aria-expanded={open}
aria-controls="menu-list"
>
Menu
</button>
{open && (
<ul id="menu-list" role="menu">
{items.map(item => (
<li key={item.id} role="menuitem">
<a href={item.path}>{item.label}</a>
</li>
))}
</ul>
)}
</nav>
);
}
Example 4: Performance Optimization
// ❌ Bad: Unstable callback recreated every render causes child re-renders
{filtered.map(product => (
<ProductCard
key={product.id}
product={product}
=> console.log(product.id)} // New function each render
/>
))}
// ✅ Good: Stable callback + memoized child
const handleSelect = useCallback((id: string) => {
console.log(id);
}, []);
const filtered = useMemo(
() => products.filter(p => p.name.toLowerCase().includes(search.toLowerCase())),
[products, search]
);
{filtered.map(product => (
<ProductCard key={product.id} product={product} />
))}
const ProductCard = memo(function ProductCard({ product, onSelect }: Props) {
return <div => onSelect(product.id)}>{product.name}</div>;
});
Example 5: TypeScript Props Review
// ❌ Bad: Loose typing and missing prop definitions
function Card({ data, onClick, children, ...rest }: any) {
return (
<div {...rest}>
<h2>{data.title}</h2>
{children}
</div>
);
}
// ✅ Good: Strict typing with proper interfaces
interface CardProps extends React.ComponentPropsWithoutRef<'article'> {
title: string;
description?: string;
variant?: 'default' | 'outlined' | 'elevated';
onAction?: (event: React.MouseEvent<HTMLButtonElement>) => void;
children: React.ReactNode;
}
function Card({
title,
description,
variant = 'default',
onAction,
children,
className,
...rest
}: CardProps) {
return (
<article className={cn('card', `card--${variant}`, className)} {...rest}>
<h2>{title}</h2>
{description && <p>{description}</p>}
{children}
{onAction && <button
</article>
);
}
Review Output Format
Structure all code review findings as follows:
1. Summary
Brief overview with an overall quality score (1-10) and key observations.
2. Critical Issues (Must Fix)
Issues causing bugs, security vulnerabilities, or broken functionality.
3. Warnings (Should Fix)
Issues that violate best practices, cause performance problems, or reduce maintainability.
4. Suggestions (Consider Improving)
Improvements for code organization, accessibility, or developer experience.
5. Positive Observations
Well-implemented patterns and good practices to acknowledge.
6. Recommendations
Prioritized next steps with code examples for the most impactful improvements.
Best Practices
- Keep components focused — single responsibility, under 200 lines
- Colocate state with the components that use it
- Use custom hooks to extract reusable logic from components
- Apply
React.memo only when measured re-render cost justifies it
- Use TanStack Query or SWR for server state instead of
useEffect + useState
- Always include cleanup functions in
useEffect when subscribing to external resources
- Write semantic HTML first, add ARIA only when native semantics are insufficient
- Use TypeScript strict mode and avoid
any in component props
- Implement error boundaries for graceful failure handling
- Prefer composition over conditional rendering complexity
Constraints and Warnings
- Respect the project's React version — avoid suggesting React 19 features for older versions
- Do not enforce a specific state management library unless the project has standardized on one
- Memoization is not always beneficial — only suggest it when re-render impact is measurable
- Accessibility recommendations should follow WCAG 2.1 AA as the baseline
- Focus on high-confidence issues — avoid false positives on subjective style choices
- Do not suggest rewriting working components without clear, measurable benefit
References
See the references/ directory for detailed review checklists and pattern documentation:
references/hooks-patterns.md — React hooks best practices and common mistakes
references/component-architecture.md — Component composition and design patterns
references/accessibility.md — Accessibility checklist and ARIA patterns for React
1---2name: react-code-review3description: Provides comprehensive code review capability for React applications, validates component architecture, hooks usage, React 19 patterns, state management, performance optimization, accessibility compliance, and TypeScript integration. Use when reviewing React code changes, before merging pull requests, after implementing new features, or for component architecture validation. Triggers on "review React code", "React code review", "check my React components".4---56# React Code Review78## Overview910This skill provides structured, comprehensive code review for React applications. It evaluates code against React 19 best practices, component architecture patterns, hook usage, accessibility standards, and production-readiness criteria. The review produces actionable findings categorized by severity (Critical, Warning, Suggestion) with concrete code examples for improvements.1112This skill delegates to the `react-software-architect-review` agent for deep architectural analysis when invoked through the agent system.1314## When to Use1516- Reviewing React components, hooks, and pages before merging17- Validating component composition and reusability patterns18- Checking proper hook usage (useState, useEffect, useMemo, useCallback)19- Reviewing React 19 patterns (use, useOptimistic, useFormStatus, Actions)20- Evaluating state management approaches (local, context, external stores)21- Assessing performance optimization (memoization, code splitting, lazy loading)22- Reviewing accessibility compliance (WCAG, semantic HTML, ARIA)23- Validating TypeScript typing for props, state, and events24- Checking Tailwind CSS and styling patterns25- After implementing new React features or refactoring component architecture2627## Instructions28291. **Identify Scope**: Determine which React components and hooks are under review. Use `glob` to discover `.tsx`/`.jsx` files and `grep` to identify component definitions, hook usage, and context providers.30312. **Analyze Component Architecture**: Verify proper component composition — check for single responsibility, appropriate size, and reusability. Look for components that are too large (>200 lines), have too many props (>7), or mix concerns.32333. **Review Hook Usage**: Validate proper hook usage — check dependency arrays in `useEffect`/`useMemo`/`useCallback`, verify cleanup functions in `useEffect`, and identify unnecessary re-renders caused by missing or incorrect memoization.34354. **Evaluate State Management**: Assess where state lives — check for proper colocation, unnecessary lifting, and appropriate use of Context vs external stores. Verify that server state uses TanStack Query, SWR, or similar libraries rather than manual `useEffect` + `useState` patterns.36375. **Check Accessibility**: Review semantic HTML usage, ARIA attributes, keyboard navigation, focus management, and screen reader compatibility. Verify that interactive elements are accessible and form inputs have proper labels.38396. **Assess Performance**: Look for unnecessary re-renders, missing `React.memo` on expensive components, improper use of `useCallback`/`useMemo`, missing code splitting, and large bundle imports.40417. **Review TypeScript Integration**: Check prop type definitions, event handler typing, generic component patterns, and proper use of utility types. Verify that `any` is not used where specific types are possible.42438. **Produce Review Report**: Generate a structured report with severity-classified findings (Critical, Warning, Suggestion), positive observations, and prioritized recommendations with code examples.4445## Examples4647### Example 1: Hook Dependency Issues4849```tsx50// ❌ Bad: Missing dependency causes stale closure51function UserProfile({ userId }: { userId: string }) {52 const [user, setUser] = useState<User | null>(null);5354 useEffect(() => {55 fetchUser(userId).then(setUser);56 }, []); // Missing userId in dependency array5758 return <div>{user?.name}</div>;59}6061// ✅ Good: Proper dependencies with cleanup62function UserProfile({ userId }: { userId: string }) {63 const [user, setUser] = useState<User | null>(null);6465 useEffect(() => {66 let cancelled = false;67 fetchUser(userId).then((data) => {68 if (!cancelled) setUser(data);69 });70 return () => { cancelled = true; };71 }, [userId]);7273 return <div>{user?.name}</div>;74}7576// ✅ Better: Use TanStack Query for server state77function UserProfile({ userId }: { userId: string }) {78 const { data: user, isLoading } = useQuery({79 queryKey: ['user', userId],80 queryFn: () => fetchUser(userId),81 });8283 if (isLoading) return <Skeleton />;84 return <div>{user?.name}</div>;85}86```8788### Example 2: Component Composition8990```tsx91// ❌ Bad: Monolithic component mixing data fetching, filtering, and rendering92function Dashboard() {93 const [users, setUsers] = useState([]);94 const [filter, setFilter] = useState('');95 useEffect(() => { /* fetch + filter + sort all in one */ }, [filter]);96 return <div>{/* 200+ lines of mixed concerns */}</div>;97}9899// ✅ Good: Composed from focused components with custom hooks100function Dashboard() {101 return (102 <div>103 <UserFilters />104 <Suspense fallback={<TableSkeleton />}>105 <UserTable />106 </Suspense>107 <UserPagination />108 </div>109 );110}111```112113### Example 3: Accessibility Review114115```tsx116// ❌ Bad: Inaccessible interactive elements117function Menu({ items }: { items: MenuItem[] }) {118 const [open, setOpen] = useState(false);119 return (120 <div>121 <div onClick={() => setOpen(!open)}>Menu</div>122 {open && (123 <div>124 {items.map(item => (125 <div key={item.id} onClick={() => navigate(item.path)}>126 {item.label}127 </div>128 ))}129 </div>130 )}131 </div>132 );133}134135// ✅ Good: Accessible with proper semantics and keyboard support136function Menu({ items }: { items: MenuItem[] }) {137 const [open, setOpen] = useState(false);138 return (139 <nav aria-label="Main navigation">140 <button141 onClick={() => setOpen(!open)}142 aria-expanded={open}143 aria-controls="menu-list"144 >145 Menu146 </button>147 {open && (148 <ul id="menu-list" role="menu">149 {items.map(item => (150 <li key={item.id} role="menuitem">151 <a href={item.path}>{item.label}</a>152 </li>153 ))}154 </ul>155 )}156 </nav>157 );158}159```160161### Example 4: Performance Optimization162163```tsx164// ❌ Bad: Unstable callback recreated every render causes child re-renders165{filtered.map(product => (166 <ProductCard167 key={product.id}168 product={product}169 onSelect={() => console.log(product.id)} // New function each render170 />171))}172173// ✅ Good: Stable callback + memoized child174const handleSelect = useCallback((id: string) => {175 console.log(id);176}, []);177178const filtered = useMemo(179 () => products.filter(p => p.name.toLowerCase().includes(search.toLowerCase())),180 [products, search]181);182183{filtered.map(product => (184 <ProductCard key={product.id} product={product} onSelect={handleSelect} />185))}186187const ProductCard = memo(function ProductCard({ product, onSelect }: Props) {188 return <div onClick={() => onSelect(product.id)}>{product.name}</div>;189});190```191192### Example 5: TypeScript Props Review193194```tsx195// ❌ Bad: Loose typing and missing prop definitions196function Card({ data, onClick, children, ...rest }: any) {197 return (198 <div onClick={onClick} {...rest}>199 <h2>{data.title}</h2>200 {children}201 </div>202 );203}204205// ✅ Good: Strict typing with proper interfaces206interface CardProps extends React.ComponentPropsWithoutRef<'article'> {207 title: string;208 description?: string;209 variant?: 'default' | 'outlined' | 'elevated';210 onAction?: (event: React.MouseEvent<HTMLButtonElement>) => void;211 children: React.ReactNode;212}213214function Card({215 title,216 description,217 variant = 'default',218 onAction,219 children,220 className,221 ...rest222}: CardProps) {223 return (224 <article className={cn('card', `card--${variant}`, className)} {...rest}>225 <h2>{title}</h2>226 {description && <p>{description}</p>}227 {children}228 {onAction && <button onClick={onAction}>Action</button>}229 </article>230 );231}232```233234## Review Output Format235236Structure all code review findings as follows:237238### 1. Summary239Brief overview with an overall quality score (1-10) and key observations.240241### 2. Critical Issues (Must Fix)242Issues causing bugs, security vulnerabilities, or broken functionality.243244### 3. Warnings (Should Fix)245Issues that violate best practices, cause performance problems, or reduce maintainability.246247### 4. Suggestions (Consider Improving)248Improvements for code organization, accessibility, or developer experience.249250### 5. Positive Observations251Well-implemented patterns and good practices to acknowledge.252253### 6. Recommendations254Prioritized next steps with code examples for the most impactful improvements.255256## Best Practices257258- Keep components focused — single responsibility, under 200 lines259- Colocate state with the components that use it260- Use custom hooks to extract reusable logic from components261- Apply `React.memo` only when measured re-render cost justifies it262- Use TanStack Query or SWR for server state instead of `useEffect` + `useState`263- Always include cleanup functions in `useEffect` when subscribing to external resources264- Write semantic HTML first, add ARIA only when native semantics are insufficient265- Use TypeScript strict mode and avoid `any` in component props266- Implement error boundaries for graceful failure handling267- Prefer composition over conditional rendering complexity268269## Constraints and Warnings270271- Respect the project's React version — avoid suggesting React 19 features for older versions272- Do not enforce a specific state management library unless the project has standardized on one273- Memoization is not always beneficial — only suggest it when re-render impact is measurable274- Accessibility recommendations should follow WCAG 2.1 AA as the baseline275- Focus on high-confidence issues — avoid false positives on subjective style choices276- Do not suggest rewriting working components without clear, measurable benefit277278## References279280See the `references/` directory for detailed review checklists and pattern documentation:281- `references/hooks-patterns.md` — React hooks best practices and common mistakes282- `references/component-architecture.md` — Component composition and design patterns283- `references/accessibility.md` — Accessibility checklist and ARIA patterns for React