---
name: code-review
description: Frontend-focused code review skill for React/TypeScript/Tailwind projects. Analyzes code quality, security vulnerabilities (XSS, CSRF), performance issues, accessibility (WCAG), React best practices, hooks usage, component architecture, responsive design, and SEO. Use when users request code review, want feedback on components, ask about frontend security, performance optimization, or accessibility compliance. Provides actionable feedback with severity levels and fix suggestions.
allowed-tools: Read, Grep, Glob, Bash
---
# Frontend Code Review
This skill provides comprehensive, production-ready code review for modern frontend applications with actionable feedback focused on React/TypeScript/Tailwind stack.
## Purpose
Transform frontend code review from manual inspection into systematic analysis covering:
1. **Frontend Security** - XSS, CSRF, sensitive data exposure, auth issues
2. **React Performance** - Re-renders, memoization, bundle size, lazy loading
3. **Code Quality** - Readability, maintainability, React best practices
4. **Component Architecture** - Layered architecture, separation of concerns, reusability
5. **Type Safety** - TypeScript usage, type correctness, runtime validation
6. **Accessibility** - WCAG compliance, keyboard navigation, screen readers
7. **Responsive Design** - Mobile-first, breakpoints, Tailwind patterns
8. **SEO & Meta** - Meta tags, semantic HTML, performance metrics
9. **Testing** - Component tests, hooks tests, edge cases
10. **State Management** - Zustand/Context patterns, React Query usage
## When to Use This Skill
Use this skill when:
- User asks for code review or feedback
- User mentions: "review", "check", "feedback", "quality", "security"
- After generating components or features
- User asks about performance or accessibility
- Before committing major changes
- Examples:
- "Review this component"
- "Is this React code optimized?"
- "Can you check for accessibility issues?"
- "How can I improve this?"
- "Review my feature implementation"
## Review Process
### Step 1: Understand Context
Before reviewing, gather context:
1. **Code Type**:
- React Component (UI, Form, List, etc.)
- Custom Hook (business logic)
- Utility function (helpers, transforms)
- API integration (React Query, fetch)
- Store/State management (Zustand, Context)
- Styling (Tailwind, CSS-in-JS)
2. **Review Scope**:
- Single component/hook
- Entire feature (multiple files)
- Page/route implementation
- Shared utilities
3. **Priority**:
- Security-critical (auth, payment forms)
- Performance-critical (large lists, complex calculations)
- User-facing (accessibility, UX)
- Internal (utilities, helpers)
### Step 2: Initial Scan
Quickly scan for obvious issues:
**Critical Issues (🚨 CRITICAL)**:
- XSS vulnerabilities (dangerouslySetInnerHTML)
- CSRF vulnerabilities (missing tokens)
- Sensitive data exposure (tokens in localStorage)
- Authentication bypass
- Hardcoded secrets/API keys
**High Priority (⚠️ HIGH)**:
- Performance bottlenecks (unnecessary re-renders, no memoization)
- Memory leaks (missing cleanup in useEffect)
- Error handling gaps
- Accessibility violations (no ARIA labels, keyboard support)
- Missing input validation
**
Medium Priority (⚡ MEDIUM)**:
- Code duplication
- Unclear component/variable names
- Missing loading/error states
- Poor TypeScript usage (any types)
- Inconsistent Tailwind usage
**Low Priority (💡 LOW)**:
- Code style inconsistencies
- Missing comments for complex logic
- Minor optimizations
- Documentation gaps
### Step 3: Deep Analysis
Perform systematic review across all dimensions:
#### 3.1 Frontend Security Review
Check against common frontend vulnerabilities:
```typescript
// ❌ BAD: XSS vulnerability
function UserComment({ comment }: { comment: string }) {
return
;
}
// ✅ GOOD: Sanitized HTML
import DOMPurify from 'dompurify';
function UserComment({ comment }: { comment: string }) {
return ;
}
// ✅ BETTER: No HTML, just text
function UserComment({ comment }: { comment: string }) {
return {comment}
;
}
// ❌ BAD: Token in localStorage (XSS vulnerable)
localStorage.setItem('token', response.token);
// ✅ GOOD: HttpOnly cookie (set by server)
// Or use secure session management library
// ❌ BAD: Hardcoded API key
const API_KEY = "pk_live_abc123xyz";
// ✅ GOOD: Environment variable
const API_KEY = process.env.NEXT_PUBLIC_API_KEY;
// ❌ BAD: No CSRF protection
async function transferMoney(to: string, amount: number) {
await fetch('/api/transfer', {
method: 'POST',
body: JSON.stringify({ to, amount })
});
}
// ✅ GOOD: Include CSRF token
async function transferMoney(to: string, amount: number) {
const csrfToken = getCsrfToken();
await fetch('/api/transfer', {
method: 'POST',
headers: {
'X-CSRF-Token': csrfToken
},
body: JSON.stringify({ to, amount })
});
}
```
**Frontend Security Checklist**:
- [ ] No `dangerouslySetInnerHTML` with user input
- [ ] No sensitive tokens in localStorage
- [ ] No hardcoded API keys or secrets
- [ ] CSRF protection for state-changing operations
- [ ] Input validation on all form inputs
- [ ] Output sanitization for user-generated content
- [ ] HTTPS enforced (check in production)
- [ ] No sensitive data in console.log
- [ ] Proper authentication checks on protected routes
- [ ] Secure password input (no autocomplete on sensitive fields)
#### 3.2 React Performance Review
Identify bottlenecks and optimization opportunities:
```typescript
// ❌ BAD: Unnecessary re-renders
function ProductList({ products }: { products: Product[] }) {
const sortedProducts = products.sort((a, b) => b.price - a.price);
// Re-sorts on every render!
return (
{sortedProducts.map(p => (
updateProduct(p.id)} />
))}
);
}
// ✅ GOOD: Memoized sorting and callbacks
function ProductList({ products }: { products: Product[] }) {
const sortedProducts = useMemo(
() => [...products].sort((a, b) => b.price - a.price),
[products]
);
const handleUpdate = useCallback((id: string) => {
updateProduct(id);
}, []);
return (
{sortedProducts.map(p => (
handleUpdate(p.id)} />
))}
);
}
// ❌ BAD: No memoization for expensive child
function ExpensiveChild({ data }: { data: Data }) {
// Complex rendering logic
return {/* ... */}
;
}
// ✅ GOOD: Memoized component
const ExpensiveChild = memo(function ExpensiveChild({ data }: { data: Data }) {
// Complex rendering logic
return {/* ... */}
;
});
// ❌ BAD: Memory leak - no cleanup
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount(c => c + 1);
}, 1000);
// No cleanup!
}, []);
return {count}
;
}
// ✅ GOOD: Proper cleanup
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount(c => c + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return {count}
;
}
// ❌ BAD: No lazy loading for large components
import HeavyChart from './HeavyChart';
import HeavyEditor from './HeavyEditor';
// ✅ GOOD: Lazy loading
const HeavyChart = lazy(() => import('./HeavyChart'));
const HeavyEditor = lazy(() => import('./HeavyEditor'));
function Dashboard() {
return (
}>
);
}
// ❌ BAD: Inline object/function props
doSomething()}
/>
// ✅ GOOD: Memoized props
const config = useMemo(() => ({ theme: 'dark' }), []);
const handleUpdate = useCallback(() => doSomething(), []);
```
**React Performance Checklist**:
- [ ] Memoization for expensive calculations (useMemo)
- [ ] Memoized callbacks (useCallback) for child components
- [ ] memo() for expensive child components
- [ ] Proper cleanup in useEffect (intervals, subscriptions)
- [ ] Lazy loading for heavy components
- [ ] Code splitting for routes
- [ ] Virtualization for long lists (react-window)
- [ ] Debouncing for frequent events (search, scroll)
- [ ] Image optimization (next/image, lazy loading)
- [ ] No inline objects/functions as props
- [ ] Proper key usage (unique IDs, not indexes)
- [ ] Avoid deep prop drilling (use Context/Zustand)
#### 3.3 Component Architecture Review
Check for proper separation of concerns:
```typescript
// ❌ BAD: Everything in one component
function UserProfile() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
// API call in component
useEffect(() => {
setLoading(true);
fetch('/api/user')
.then(res => res.json())
.then(data => {
setUser(data);
setLoading(false);
})
.catch(err => {
setError(err);
setLoading(false);
});
}, []);
// Business logic in component
const fullName = user ? `${user.firstName} ${user.lastName}` : '';
const isAdult = user?.age >= 18;
// Validation in component
const validateEmail = (email: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
if (loading) return Loading...
;
if (error) return Error: {error.message}
;
return {fullName}
;
}
// ✅ GOOD: Layered architecture
// api/userApi.ts (Data Access Layer)
export const userApi = {
getUser: async (): Promise => {
const response = await fetch('/api/user');
if (!response.ok) throw new Error('Failed to fetch user');
return response.json();
}
};
// hooks/useUser.ts (Business Logic Layer)
export function useUser() {
const { data: user, isLoading, error } = useQuery({
queryKey: ['user'],
queryFn: userApi.getUser
});
const fullName = user ? `${user.firstName} ${user.lastName}` : '';
const isAdult = user?.age >= 18;
return { user, fullName, isAdult, isLoading, error };
}
// utils/validation.ts (Business Logic Layer)
export const emailSchema = z.string().email();
export function validateEmail(email: string): boolean {
return emailSchema.safeParse(email).success;
}
// components/UserProfile.tsx (Presentation Layer)
export function UserProfile() {
const { fullName, isLoading, error } = useUser();
if (isLoading) return ;
if (error) return ;
return {fullName}
;
}
```
**Architecture Checklist**:
- [ ] Layered architecture (Presentation, Business Logic, Data Access)
- [ ] No API calls directly in components
- [ ] No business logic in components
- [ ] Hooks for reusable logic
- [ ] Zustand/Context for global state
- [ ] React Query for server state
- [ ] No circular dependencies
- [ ] Single Responsibility Principle
- [ ] Components are small and focused (<200 lines)
- [ ] Proper prop types (TypeScript interfaces)
#### 3.4 Type Safety Review
Check for proper TypeScript usage:
```typescript
// ❌ BAD: Using 'any'
function processData(data: any) {
return data.value;
}
// ✅ GOOD: Proper types
interface ProcessData {
value: string;
count: number;
}
function processData(data: ProcessData): string {
return data.value;
}
// ❌ BAD: Non-null assertion without justification
function getUser(users: User[], id: string) {
return users.find(u => u.id === id)!.name; // Dangerous!
}
// ✅ GOOD: Proper null handling
function getUser(users: User[], id: string): string | null {
return users.find(u => u.id === id)?.name ?? null;
}
// ❌ BAD: No runtime validation
function LoginForm() {
const handleSubmit = (data: unknown) => {
// Assuming data structure without validation
login(data);
};
}
// ✅ GOOD: Runtime validation with Zod
import { z } from 'zod';
const loginSchema = z.object({
email: z.string().email(),
password: z.string().min(8)
});
type LoginData = z.infer;
function LoginForm() {
const handleSubmit = (data: unknown) => {
try {
const validatedData = loginSchema.parse(data);
login(validatedData); // Type-safe
} catch (err) {
// Handle validation errors
}
};
}
// ❌ BAD: Implicit any in event handlers
setValue(e.target.value)} />
// 'e' is implicitly 'any' in some configs
// ✅ GOOD: Explicit types
) => setValue(e.target.value)} />
// ✅ BETTER: Typed handler
const handleChange = (e: React.ChangeEvent) => {
setValue(e.target.value);
};
```
**Type Safety Checklist**:
- [ ] No 'any' types (except where truly necessary)
- [ ] No non-null assertions (!) without justification
- [ ] Proper interface/type definitions for props
- [ ] Runtime validation with Zod for external data
- [ ] Type guards for discriminated unions
- [ ] Explicit types for event handlers
- [ ] Strict mode enabled in tsconfig.json
- [ ] No implicit any
- [ ] Proper generic usage in hooks
#### 3.5 Accessibility Review
Check WCAG 2.1 AA compliance:
```typescript
// ❌ BAD: No keyboard support, no ARIA labels
// ✅ GOOD: Semantic HTML, ARIA labels, keyboard support
// ❌ BAD: No alt text, poor color contrast

Click here
// ✅ GOOD: Alt text, proper contrast (WCAG AA)
// ❌ BAD: Custom select without keyboard navigation
function CustomSelect({ options }) {
const [open, setOpen] = useState(false);
return (
setOpen(!open)}>
{open && options.map(opt => (
selectOption(opt)}>{opt}
))}
);
}
// ✅ GOOD: Accessible custom select
function CustomSelect({ options }: { options: string[] }) {
const [open, setOpen] = useState(false);
const [selectedIndex, setSelectedIndex] = useState(0);
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'ArrowDown') {
setSelectedIndex(i => Math.min(i + 1, options.length - 1));
} else if (e.key === 'ArrowUp') {
setSelectedIndex(i => Math.max(i - 1, 0));
} else if (e.key === 'Enter') {
selectOption(options[selectedIndex]);
}
};
return (
setOpen(!open)}
>
{open && (
{options.map((opt, index) => (
- selectOption(opt)}
>
{opt}
))}
)}
);
}
```
**Accessibility Checklist**:
- [ ] Semantic HTML elements (button, nav, main, etc.)
- [ ] ARIA labels for interactive elements
- [ ] Keyboard navigation support (Tab, Enter, Arrows)
- [ ] Focus management (visible focus states)
- [ ] Color contrast meets WCAG AA (4.5:1 for text)
- [ ] Alt text for all images
- [ ] Form labels properly associated (htmlFor)
- [ ] Error messages use role="alert"
- [ ] No keyboard traps
- [ ] Skip links for main content
- [ ] Headings in logical order (h1, h2, h3)
- [ ] Interactive elements have proper roles
#### 3.6 Responsive Design Review
Check Tailwind/responsive patterns:
```typescript
// ❌ BAD: Fixed widths, no responsive classes
// ✅ GOOD: Responsive Tailwind classes
// ❌ BAD: No mobile considerations
// ✅ GOOD: Mobile-first responsive layout
// ❌ BAD: Fixed font sizes
Title
// ✅ GOOD: Responsive typography
Title
```
**Responsive Design Checklist**:
- [ ] Mobile-first approach (base styles for mobile)
- [ ] Proper breakpoints (sm, md, lg, xl, 2xl)
- [ ] Flexible layouts (flex, grid)
- [ ] Responsive images (w-full h-auto, object-fit)
- [ ] Responsive typography (responsive text sizes)
- [ ] Touch-friendly hit areas (min 44x44px)
- [ ] No horizontal scrolling on mobile
- [ ] Tested on multiple screen sizes
#### 3.7 Code Quality Review
Evaluate readability and maintainability:
```typescript
// ❌ BAD: Unclear names, deeply nested
function p(d) {
if (d) {
if (d.u) {
if (d.u.n) {
if (d.u.n.length > 0) {
return d.u.n;
}
}
}
}
return 'Anonymous';
}
// ✅ GOOD: Clear names, early returns
function getUserName(data: UserData | null): string {
if (!data?.user?.name) return 'Anonymous';
if (data.user.name.length === 0) return 'Anonymous';
return data.user.name;
}
// ✅ BETTER: Optional chaining
function getUserName(data: UserData | null): string {
return data?.user?.name || 'Anonymous';
}
// ❌ BAD: Magic numbers and strings
if (user.age > 18 && status === 'active') {
grantAccess();
}
// ✅ GOOD: Named constants
const MINIMUM_AGE = 18;
const USER_STATUS = {
ACTIVE: 'active',
INACTIVE: 'inactive'
} as const;
if (user.age > MINIMUM_AGE && status === USER_STATUS.ACTIVE) {
grantAccess();
}
// ❌ BAD: Long component with multiple responsibilities
function UserDashboard() {
// 300 lines of code handling:
// - User data fetching
// - Analytics tracking
// - Notification handling
// - UI rendering
}
// ✅ GOOD: Split into focused components
function UserDashboard() {
return (
);
}
```
**Code Quality Checklist**:
- [ ] Clear, descriptive names (getUserName, not gn)
- [ ] No magic numbers or strings (use constants)
- [ ] Early returns instead of deep nesting
- [ ] Small functions/components (<100 lines)
- [ ] Single Responsibility Principle
- [ ] DRY (Don't Repeat Yourself)
- [ ] No commented-out code
- [ ] No console.log in production code
- [ ] Consistent code style
- [ ] Proper error handling
#### 3.8 React Hooks Best Practices
```typescript
// ❌ BAD: Missing dependencies
useEffect(() => {
fetchData(userId, filter);
}, []); // userId and filter are missing!
// ✅ GOOD: All dependencies included
useEffect(() => {
fetchData(userId, filter);
}, [userId, filter]);
// ❌ BAD: Object dependency (recreated every render)
const options = { sort: 'asc', limit: 10 };
useEffect(() => {
fetchData(options);
}, [options]); // Will run every render!
// ✅ GOOD: Memoized object
const options = useMemo(() => ({
sort: 'asc',
limit: 10
}), []);
useEffect(() => {
fetchData(options);
}, [options]);
// ❌ BAD: Stale closure
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount(count + 1); // Uses stale count!
}, 1000);
return () => clearInterval(interval);
}, []); // Empty deps, count is stale
return {count}
;
}
// ✅ GOOD: Functional update
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount(c => c + 1); // Uses current count
}, 1000);
return () => clearInterval(interval);
}, []); // Empty deps OK now
return {count}
;
}
```
### Step 4: Provide Structured Feedback
Format review results clearly:
```markdown
## Code Review: [Component/Feature Name]
### Summary
[Brief overall assessment: Excellent/Good/Needs Improvement/Critical Issues]
### Critical Issues 🚨
1. **[Issue Title]** - [file:line]
- **Problem**: [Description]
- **Impact**: [Security/Performance/Accessibility]
- **Fix**:
```typescript
// Suggested fix
```
### High Priority ⚠️
[Same format]
### Medium Priority ⚡
[Same format]
### Low Priority 💡
[Same format]
### What's Good ✅
- [Highlight positive aspects]
- [Good practices used]
### Recommendations
1. [Actionable improvement]
2. [Actionable improvement]
### Overall Score
- Security: 8/10
- Performance: 7/10
- Code Quality: 9/10
- Accessibility: 6/10
- Type Safety: 8/10
**Overall: 7.6/10**
```
## Integration with Other Skills
### With feature-builder
- Review complete features (UI + business logic + API)
- Ensure layered architecture is properly implemented
### With react-component-generator
- Review generated components
- Check template usage and customization
### With ui-analyzer
- Review UI code generated from design screenshots
- Verify responsive design implementation
## Best Practices
1. **Be Constructive**: Always provide actionable fixes with code examples
2. **Prioritize**: Focus on critical issues (security, accessibility) first
3. **Explain Why**: Help user understand the reasoning and impact
4. **Show Before/After**: Provide clear code examples
5. **Be Specific**: Reference exact lines and files
6. **Balance**: Highlight what's good too
7. **Be Practical**: Consider project constraints and deadlines
8. **Educate**: Explain React/frontend concepts when needed
## Severity Levels
- 🚨 **CRITICAL**: Security vulnerabilities (XSS, CSRF), data exposure, crashes
- ⚠️ **HIGH**: Performance issues (memory leaks), accessibility violations, broken UX
- ⚡ **MEDIUM**: Code quality, maintainability, missing TypeScript types
- 💡 **LOW**: Code style, documentation, minor optimizations
## Reference Files
- `references/frontend-security.md` - Frontend security best practices
- `references/react-patterns.md` - React patterns and anti-patterns
- `references/accessibility-guide.md` - WCAG compliance guide
This skill enables thorough, professional frontend code reviews that improve code quality, security, and user experience!