Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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".
.claude/skills/giuseppe-trisciuoglio-react-code-review/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 152% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 65% | 0% |
| case-21 | ✓→✓ | = Same ✓ | 57% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 99% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 89% | 0% |
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.
glob to discover .tsx/.jsx files and grep to identify component definitions, hook usage, and context providers.useEffect/useMemo/useCallback, verify cleanup functions in useEffect, and identify unnecessary re-renders caused by missing or incorrect memoization.useEffect + useState patterns.React.memo on expensive components, improper use of useCallback/useMemo, missing code splitting, and large bundle imports.any is not used where specific types are possible.tsx// ❌ 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>; }
tsx// ❌ 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> ); }
tsx// ❌ Bad: Inaccessible interactive elements function Menu({ items }: { items: MenuItem[] }) { const [open, setOpen] = useState(false); return ( <div> <div onClick={() => setOpen(!open)}>Menu</div> {open && ( <div> {items.map(item => ( <div key={item.id} onClick={() => 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 onClick={() => 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> ); }
tsx// ❌ Bad: Unstable callback recreated every render causes child re-renders {filtered.map(product => ( <ProductCard key={product.id} product={product} onSelect={() => 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} onSelect={handleSelect} /> ))} const ProductCard = memo(function ProductCard({ product, onSelect }: Props) { return <div onClick={() => onSelect(product.id)}>{product.name}</div>; });
tsx// ❌ Bad: Loose typing and missing prop definitions function Card({ data, onClick, children, ...rest }: any) { return ( <div onClick={onClick} {...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 onClick={onAction}>Action</button>} </article> ); }
Structure all code review findings as follows:
Brief overview with an overall quality score (1-10) and key observations.
Issues causing bugs, security vulnerabilities, or broken functionality.
Issues that violate best practices, cause performance problems, or reduce maintainability.
Improvements for code organization, accessibility, or developer experience.
Well-implemented patterns and good practices to acknowledge.
Prioritized next steps with code examples for the most impactful improvements.
React.memo only when measured re-render cost justifies ituseEffect + useStateuseEffect when subscribing to external resourcesany in component propsSee the references/ directory for detailed review checklists and pattern documentation:
references/hooks-patterns.md — React hooks best practices and common mistakesreferences/component-architecture.md — Component composition and design patternsreferences/accessibility.md — Accessibility checklist and ARIA patterns for React| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-21 | pass→pass | 21,236 | 18,375 | -13% | 1 | 1 | 0% | 3,427 | 5,372 | +57% | 0 | 0 | — |
case-01 | fail→fail | 18,463 | 5,064 | -73% | 1 | 1 | 0% | 3,207 | 2,659 | -17% | 0 | 0 | — |
case-02 | fail→pass | 12,747 | 13,379 | +5% | 1 | 1 | 0% | 1,951 | 4,911 | +152% | 0 | 0 | — |
case-03 | fail→fail | 15,517 | 12,369 | -20% | 1 | 1 | 0% | 2,553 | 4,589 | +80% | 0 | 0 | — |
case-04 | pass→pass | 17,289 | 13,404 | -22% | 1 | 1 | 0% | 2,424 | 4,830 | +99% | 0 | 0 | — |
case-05 | pass→pass | 13,048 | 11,756 | -10% | 1 | 1 | 0% | 2,367 | 4,476 | +89% | 0 | 0 | — |
case-06 | pass→pass | 11,210 | 9,639 | -14% | 1 | 1 | 0% | 2,113 | 4,155 | +97% | 0 | 0 | — |
case-07 | pass→pass | 11,628 | 13,626 | +17% | 1 | 1 | 0% | 1,979 | 5,048 | +155% | 0 | 0 | — |
case-08 | pass→pass | 11,941 | 3,542 | -70% | 1 | 1 | 0% | 2,030 | 2,820 | +39% | 0 | 0 | — |
case-09 | pass→pass | 16,583 | 16,097 | -3% | 1 | 1 | 0% | 2,856 | 5,330 | +87% | 0 | 0 | — |
case-10 | pass→pass | 11,178 | 8,838 | -21% | 1 | 1 | 0% | 1,682 | 3,753 | +123% | 0 | 0 | — |
case-11 | fail→fail | 11,544 | 5,519 | -52% | 1 | 1 | 0% | 2,082 | 3,328 | +60% | 0 | 0 | — |
case-12 | fail→pass | 14,642 | 8,784 | -40% | 1 | 1 | 0% | 2,407 | 3,979 | +65% | 0 | 0 | — |
case-13 | pass→pass | 14,205 | 12,424 | -13% | 1 | 1 | 0% | 2,240 | 4,568 | +104% | 0 | 0 | — |
case-14 | pass→pass | 11,600 | 68,061 | +487% | 1 | 1 | 0% | 2,060 | 3,539 | +72% | 0 | 0 | — |
case-15 | pass→pass | 13,956 | 9,344 | -33% | 1 | 1 | 0% | 1,550 | 3,976 | +157% | 0 | 0 | — |
case-22 | pass→pass | 10,458 | 15,831 | +51% | 1 | 1 | 0% | 1,911 | 5,932 | +210% | 0 | 0 | — |
case-16 | pass→pass | 11,782 | 11,076 | -6% | 1 | 1 | 0% | 1,917 | 4,291 | +124% | 0 | 0 | — |
case-17 | pass→pass | 13,707 | 12,205 | -11% | 1 | 1 | 0% | 2,380 | 4,411 | +85% | 0 | 0 | — |
case-18 | pass→pass | 10,422 | 11,046 | +6% | 1 | 1 | 0% | 1,630 | 4,211 | +158% | 0 | 0 | — |
case-19 | pass→pass | 11,479 | 9,584 | -17% | 1 | 1 | 0% | 1,778 | 3,903 | +120% | 0 | 0 | — |
case-20 | pass→pass | 19,416 | 13,560 | -30% | 1 | 1 | 0% | 1,657 | 4,837 | +192% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 21 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +9 percentage points is the difference between those two pass rates over the 21 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.