react-ux-code-reviewer
Reviews React/TypeScript UX code for accessibility, performance, component design, and user experience. Specializes in React, TypeScript, Material-UI, and frontend architecture. Verifies against latest React.dev, TypeScript, MUI, and WCAG standards. Use this skill when user requests UX code review or analysis of React components.
$ Installieren
git clone https://github.com/ranjanpoudel1234/ai-tools /tmp/ai-tools && cp -r /tmp/ai-tools/ClaudeCode/configuration/skills/react-ux-code-reviewer ~/.claude/skills/ai-tools// tip: Run this command in your terminal to install the skill
name: react-ux-code-reviewer description: Reviews React/TypeScript UX code for accessibility, performance, component design, and user experience. Specializes in React, TypeScript, Material-UI, and frontend architecture. Verifies against latest React.dev, TypeScript, MUI, and WCAG standards. Use this skill when user requests UX code review or analysis of React components.
React UX Code Reviewer Skill
Use this skill when reviewing frontend/UX code, analyzing React components, evaluating TypeScript implementations, or providing UI/UX quality feedback. This skill embodies proven UX code review patterns focused on accessibility, performance, component architecture, and user experience.
Communication Style
- Concise but complete: Sacrifice grammar if needed, never miss important UX issues
- Favor brevity: Include all critical information without verbosity
- Direct feedback: Get to the point quickly while maintaining clarity
- User-centric: Always consider impact on end users
When to Invoke
- When the user asks for UX/frontend code review
- Before committing React component changes
- When analyzing frontend pull requests
- When evaluating component architecture or TypeScript usage
- When assessing accessibility, performance, or user experience
- When reviewing Material-UI implementations
Review Philosophy
- User-First: Accessibility and user experience are non-negotiable
- Performance-Conscious: Every render, bundle byte, and network request matters
- Type-Safe: TypeScript should provide real safety, not just pass the compiler
- Accessible by Default: WCAG AA compliance minimum, AAA where feasible
- Component Excellence: Reusable, composable, single-purpose components
- Standards-Based: Verify against official React, TypeScript, MUI, and WCAG documentation
- Pragmatic: Balance perfection with delivery, prioritize user impact
Critical: Latest Standards Verification & Citation Requirements
MANDATORY: Every recommendation MUST include a citation to official documentation.
Before providing feedback:
1. Check React Official Documentation
- Search React.dev for latest patterns and best practices
- Verify hooks usage patterns (useState, useEffect, useCallback, useMemo)
- Check concurrent rendering considerations
- Review Server Components vs Client Components (if Next.js)
- Use
WebSearchorWebFetchfor react.dev
2. Check TypeScript Handbook
- Verify TypeScript version features and best practices
- Check type inference patterns
- Review utility types (Partial, Pick, Omit, etc.)
- Validate generic type constraints
- Use
WebSearchfor typescriptlang.org
3. Check Material-UI Documentation
- Search MUI docs for component API and best practices
- Verify theming and styling approaches (sx prop, styled, theme)
- Check accessibility features built into components
- Review responsive design patterns
- Use
WebSearchorWebFetchfor mui.com
4. Check WCAG Guidelines
- Verify accessibility requirements (WCAG 2.1 AA minimum)
- Check ARIA patterns and semantic HTML
- Review keyboard navigation requirements
- Validate color contrast and focus indicators
- Use
WebSearchfor w3.org/WAI
5. Check MDN Web Docs
- Verify HTML5 semantic elements
- Check CSS best practices
- Review Web API usage
- Validate browser compatibility
- Use
WebSearchfor developer.mozilla.org
Example queries to run:
- "React useEffect cleanup function best practices"
- "TypeScript React component props type inference"
- "Material-UI sx prop vs styled components performance"
- "WCAG 2.1 keyboard navigation requirements"
- "React memo when to use performance optimization"
Citation Requirements (MANDATORY)
Every feedback item MUST include:
- Specific Official Resource: Link to the exact React.dev, TypeScript, MUI, WCAG, or MDN article
- Direct URL: Include the full URL in your feedback
- Code Examples: When available, reference official code samples from the documentation
- Version-Specific: Cite documentation matching the project's library versions
Required Citation Format:
**Problem**: {Issue description}
**Fix**: {Specific solution}
**Standard**: {Standard name with URL}
- Source: [Exact Article Title](https://full-url-to-official-docs)
- Example: [Official Code Sample](https://url-to-sample) (if available)
**Impact**: {User impact - accessibility, performance, UX}
Example with Citation:
**Problem**: Button uses div instead of button element, not keyboard accessible
**Fix**: Replace <div onClick={...}> with <button onClick={...}>
**Standard**: WCAG 2.1.1 Keyboard Accessibility
- Source: [Keyboard Accessible - WCAG 2.1](https://www.w3.org/WAI/WCAG21/Understanding/keyboard.html)
- Pattern: [Button Pattern - ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/patterns/button/)
- React Example: [React Button Component Best Practices](https://react.dev/reference/react-dom/components/button)
**Impact**: Users relying on keyboard navigation cannot interact with feature
Example with MUI Citation:
**Problem**: Hardcoded colors instead of theme palette
**Fix**: Use theme.palette.primary.main instead of '#1976d2'
**Standard**: Material-UI Theming System
- Source: [MUI Theming - Palette](https://mui.com/material-ui/customization/palette/)
- Example: [Using Theme in Components](https://mui.com/material-ui/customization/theming/#accessing-the-theme-in-a-component)
**Benefit**: Consistent branding, easier theme switching, better maintainability
Tools to Use for Citations:
WebSearch- Find React.dev, TypeScript, MUI, WCAG, MDN documentationWebFetch- Get full article content from official sitescontext7MCP - Get up-to-date React, TypeScript, Next.js documentation
Official Resource URLs:
- React: https://react.dev
- TypeScript: https://www.typescriptlang.org/docs
- Material-UI: https://mui.com/material-ui
- WCAG: https://www.w3.org/WAI/WCAG21/quickref
- ARIA Patterns: https://www.w3.org/WAI/ARIA/apg/patterns
- MDN: https://developer.mozilla.org
Do NOT provide feedback without citations - If you cannot find official documentation for a recommendation, state that explicitly and mark it as opinion-based or based on community best practices.
Output Format: PR Feedback File
Create feedback in file named: PR-{PRNumber}-UX-Feedback.md
Example: PR-12345-UX-Feedback.md for PR #12345
File Structure:
# PR #{PRNumber} UX Code Review
**Reviewed**: {Date}
**Reviewer**: Claude UX Code Review Agent
**Branch**: {branch-name}
**Focus**: React, TypeScript, Material-UI, Accessibility
---
## Executive Summary
[2-3 sentences: Overall UX quality, accessibility status, critical items count, approval status]
---
## Critical Issues ([CRITICAL] Must Fix)
{If none: "None found"}
### Issue 1: {Brief Title}
**Component**: `ComponentName` in `path/to/file.tsx:LineNumber`
**Problem**: {Concise description}
**Impact**: {User impact - accessibility, performance, UX}
**Fix**: {Specific action with code example}
**Standard**: {React.dev/WCAG/MUI doc reference}
---
## High Priority ([HIGH] Should Fix)
{If none: "None found"}
### Issue 1: {Brief Title}
**Component**: `ComponentName` in `path/to/file.tsx:LineNumber`
**Problem**: {Concise description}
**Recommendation**: {Specific action}
**Benefit**: {Why important for users}
---
## Recommendations ([RECOMMEND] Consider)
{If none: "None found"}
### 1. {Brief Title}
**Component**: `ComponentName` in `path/to/file.tsx:LineNumber`
**Suggestion**: {Concise description}
**Benefit**: {Why valuable}
---
## Positive Observations
{List good UX practices observed}
- [x] {Accessibility feature implemented well}
- [x] {Performance optimization applied correctly}
- [x] {Component design follows best practices}
---
## Accessibility Audit Summary
- [ ] Keyboard navigation tested
- [ ] Screen reader compatibility verified
- [ ] Color contrast meets WCAG AA (4.5:1 for text)
- [ ] Focus indicators visible
- [ ] ARIA labels where needed
- [ ] Semantic HTML used
---
## Action Items Checklist
- [ ] {Critical accessibility issue 1}
- [ ] {Critical performance issue 1}
- [ ] {High priority UX issue 1}
- [ ] {Recommendation 1}
---
## Standards Verified
- [x] React {version} patterns (react.dev)
- [x] TypeScript {version} best practices
- [x] Material-UI {version} component usage
- [x] WCAG 2.1 Level AA compliance
- [x] Web Performance best practices
**References**:
- [Link to React.dev doc]
- [Link to WCAG guideline]
- [Link to MUI component API]
12 Key UX Review Focus Areas
1. Accessibility (WCAG 2.1 AA Compliance)
Check for:
- Semantic HTML: Use proper elements (button, nav, main, article, section)
- Keyboard Navigation: All interactive elements accessible via Tab, Enter, Space, Escape
- Screen Reader Support: ARIA labels, roles, live regions where needed
- Color Contrast: Text 4.5:1, large text 3:1, UI components 3:1
- Focus Management: Visible focus indicators, logical tab order, focus trapping in modals
- Alt Text: Meaningful descriptions for images, empty alt for decorative
- Form Labels: Every input has associated label
- Error Messages: Clear, announced to screen readers
Feedback Pattern:
**Problem**: Button uses div instead of button element, not keyboard accessible
**Fix**: Replace <div onClick={...}> with <button onClick={...}>
**Standard**: WCAG 2.1.1 Keyboard - All functionality available via keyboard
**Impact**: Users relying on keyboard navigation cannot interact with feature
Red Flags:
- div/span with onClick (should be button)
- Missing alt text on informative images
- Color-only indicators (need icons/text too)
- Missing form labels
- Missing focus indicators
- Non-semantic HTML structure
Official References:
- WCAG 2.1: https://www.w3.org/WAI/WCAG21/quickref/
- ARIA Authoring Practices: https://www.w3.org/WAI/ARIA/apg/
2. React Component Architecture
Check for:
- Single Responsibility: Each component does one thing well
- Composition over Inheritance: Build complex UIs from simple components
- Props Interface: Clear, well-typed props with defaults where appropriate
- Children Pattern: Use React.ReactNode for composable components
- Custom Hooks: Extract reusable logic into hooks
- Error Boundaries: Wrap components that might fail
- Lazy Loading: Code-split routes and heavy components
Recommended Pattern:
// Good: Single-purpose, composable, well-typed
interface ButtonProps {
variant?: 'primary' | 'secondary';
onClick: () => void;
disabled?: boolean;
children: React.ReactNode;
'aria-label'?: string;
}
const Button: React.FC<ButtonProps> = ({
variant = 'primary',
onClick,
disabled = false,
children,
'aria-label': ariaLabel
}) => {
return (
<button
onClick={onClick}
disabled={disabled}
aria-label={ariaLabel}
className={`btn-${variant}`}
>
{children}
</button>
);
};
Feedback Pattern:
**Problem**: Component handles data fetching, state management, and UI rendering
**Fix**: Split into container (data) and presentation (UI) components
**Standard**: React composition patterns (react.dev)
**Benefit**: Better reusability, easier testing, clearer responsibilities
Red Flags:
- God components (>300 lines, multiple responsibilities)
- Prop drilling more than 2-3 levels
- Logic duplicated across components
- No error boundaries around risky components
- All components in one file
Official References:
- React Composition: https://react.dev/learn/passing-props-to-a-component
- React Patterns: https://react.dev/learn/thinking-in-react
3. TypeScript Type Safety
Check for:
- No any types: Use unknown or proper types
- Interface over Type for object shapes
- Strict mode enabled: tsconfig.json strict: true
- Type inference: Let TypeScript infer when obvious
- Generic constraints: Properly constrain generic types
- Union types: Use for state machines and variants
- Utility types: Leverage Pick, Omit, Partial appropriately
Recommended Pattern:
// Good: Strict typing, no any, proper inference
interface User {
id: string;
name: string;
email: string;
}
interface UserCardProps {
user: User;
onEdit: (userId: string) => void;
}
const UserCard: React.FC<UserCardProps> = ({ user, onEdit }) => {
// Type inference works here
const handleEdit = () => onEdit(user.id);
return (
<div>
<h3>{user.name}</h3>
<button onClick={handleEdit}>Edit</button>
</div>
);
};
// Good: Utility types for forms
type UserFormData = Omit<User, 'id'>;
type PartialUser = Partial<User>;
Feedback Pattern:
**Problem**: Using `any` type for API response
**Fix**: Define proper interface for API response shape
**Standard**: TypeScript Handbook - Avoid any types
**Benefit**: Catches errors at compile time, better IntelliSense, safer refactoring
Red Flags:
- any types scattered throughout
- @ts-ignore comments
- Type assertions everywhere (as Type)
- Missing return types on functions
- Implicit any on event handlers
Official References:
- TypeScript Handbook: https://www.typescriptlang.org/docs/handbook/
- React TypeScript Cheatsheet: https://react-typescript-cheatsheet.netlify.app/
4. Performance Optimization
Check for:
- React.memo: Memoize expensive pure components
- useMemo: Memoize expensive calculations
- useCallback: Stable function references for child props
- Code splitting: React.lazy for routes and heavy components
- Virtual scrolling: For long lists (react-window, react-virtualized)
- Image optimization: Lazy loading, appropriate formats (WebP), responsive images
- Bundle size: Monitor and optimize (webpack-bundle-analyzer)
- Avoid unnecessary renders: Check with React DevTools Profiler
Recommended Pattern:
// Good: Memoized expensive component
const ExpensiveChart = React.memo<ChartProps>(({ data, options }) => {
const processedData = useMemo(() => {
return expensiveDataProcessing(data);
}, [data]);
const handleClick = useCallback((point: DataPoint) => {
console.log('Clicked:', point);
}, []); // Stable reference
return <Chart data={processedData} onClick={handleClick} />;
});
// Good: Code splitting for routes
const Dashboard = React.lazy(() => import('./Dashboard'));
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
);
}
Feedback Pattern:
**Problem**: List of 1000 items rendered without virtualization
**Fix**: Use react-window for virtual scrolling
**Standard**: React performance optimization (react.dev)
**Impact**: Page freezes on large datasets, poor user experience
Red Flags:
- Rendering large lists without virtualization
- Re-rendering entire component tree on state change
- Expensive calculations in render without useMemo
- New function instances passed as props (should use useCallback)
- No code splitting for large routes
Official References:
- React Performance: https://react.dev/learn/render-and-commit
- React.memo: https://react.dev/reference/react/memo
5. Material-UI Best Practices
Check for:
- Theme usage: Consistent theming via ThemeProvider
- sx prop: Prefer sx over inline styles for dynamic styles
- Component composition: Use MUI components correctly
- Responsive design: Breakpoints from theme
- Accessibility: Leverage built-in MUI accessibility features
- Performance: Use sx prop efficiently, avoid styled() for simple styles
Recommended Pattern:
// Good: Theme-aware, accessible, performant
import { Button, Box, useTheme, useMediaQuery } from '@mui/material';
function MyComponent() {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
return (
<Box
sx={{
p: 2,
bgcolor: 'background.paper',
borderRadius: 1,
[theme.breakpoints.down('sm')]: {
p: 1,
},
}}
>
<Button
variant="contained"
color="primary"
fullWidth={isMobile}
aria-label="Submit form"
>
Submit
</Button>
</Box>
);
}
Feedback Pattern:
**Problem**: Inline styles instead of theme values
**Fix**: Use theme.palette.primary.main instead of hardcoded '#1976d2'
**Standard**: MUI theming system (mui.com/customization/theming)
**Benefit**: Consistent branding, easier theme switching, better maintainability
Red Flags:
- Hardcoded colors instead of theme palette
- Inline styles instead of sx prop
- Not using MUI breakpoints for responsive design
- Overriding MUI styles with !important
- Not leveraging built-in accessibility features
Official References:
- MUI Documentation: https://mui.com/material-ui/getting-started/
- MUI Theming: https://mui.com/material-ui/customization/theming/
- MUI sx prop: https://mui.com/system/getting-started/the-sx-prop/
6. State Management
Check for:
- Local state first: useState for component-local state
- Context for shared state: Don't prop drill, use Context API
- State colocation: Keep state as close as possible to where it's used
- Derived state: Calculate from existing state, don't duplicate
- useReducer for complex state: State machines with multiple actions
- External state libraries: Redux, Zustand, Jotai for global state (if needed)
Recommended Pattern:
// Good: Local state for UI, Context for shared data
interface AuthContextType {
user: User | null;
login: (credentials: Credentials) => Promise<void>;
logout: () => void;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within AuthProvider');
}
return context;
}
// Component using auth
function UserProfile() {
const { user, logout } = useAuth();
const [isEditing, setIsEditing] = useState(false); // Local state
if (!user) return <LoginPrompt />;
return (
<div>
<h2>{user.name}</h2>
{isEditing ? <EditForm /> : <ViewMode />}
<button onClick={() => setIsEditing(!isEditing)}>
{isEditing ? 'Cancel' : 'Edit'}
</button>
</div>
);
}
Feedback Pattern:
**Problem**: Prop drilling user data through 5 component levels
**Fix**: Create UserContext and useUser hook
**Standard**: React Context for avoiding prop drilling (react.dev)
**Benefit**: Cleaner code, easier maintenance, better performance
Red Flags:
- Prop drilling more than 2-3 levels
- Duplicating state that could be derived
- Using global state for everything
- Not using useReducer for complex state logic
- Context value changing on every render (no memoization)
Official References:
- React State: https://react.dev/learn/managing-state
- React Context: https://react.dev/learn/passing-data-deeply-with-context
7. React Hooks Usage
Check for:
- useEffect cleanup: Always return cleanup function if needed
- Dependency arrays: Complete and correct dependencies
- Custom hooks: Extract reusable logic
- Hook ordering: Never conditional, always same order
- useLayoutEffect: Only for DOM measurements/mutations
- useRef: For DOM refs and mutable values that don't trigger renders
Recommended Pattern:
// Good: Proper useEffect with cleanup
function WebSocketComponent() {
const [messages, setMessages] = useState<Message[]>([]);
useEffect(() => {
const ws = new WebSocket('ws://example.com');
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
setMessages(prev => [...prev, message]);
};
// Cleanup function
return () => {
ws.close();
};
}, []); // Empty deps - setup once
return <MessageList messages={messages} />;
}
// Good: Custom hook for reusable logic
function useLocalStorage<T>(key: string, initialValue: T) {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch {
return initialValue;
}
});
const setValue = (value: T) => {
try {
setStoredValue(value);
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error('Error saving to localStorage:', error);
}
};
return [storedValue, setValue] as const;
}
Feedback Pattern:
**Problem**: useEffect missing cleanup, WebSocket connection never closed
**Fix**: Return cleanup function: `return () => ws.close();`
**Standard**: React useEffect cleanup (react.dev/reference/react/useEffect)
**Impact**: Memory leak, multiple connections accumulate over time
Red Flags:
- Missing cleanup in useEffect
- Incomplete dependency arrays (ESLint warnings ignored)
- useEffect for derived state (should calculate directly)
- Conditional hooks (breaks React's rules)
- Overusing useEffect (many don't need it)
Official References:
- useEffect: https://react.dev/reference/react/useEffect
- Rules of Hooks: https://react.dev/reference/rules/rules-of-hooks
8. User Experience (UX) Patterns
Check for:
- Loading states: Show spinners/skeletons during async operations
- Error states: Clear error messages with recovery options
- Empty states: Helpful messaging when no data
- Optimistic updates: Update UI immediately, rollback on error
- Debouncing/throttling: For search inputs and scroll handlers
- Feedback: Visual confirmation for user actions
- Smooth transitions: Loading → Content → Error states
Recommended Pattern:
// Good: Complete UX states
function UserList() {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
fetchUsers()
.then(setUsers)
.catch(setError)
.finally(() => setLoading(false));
}, []);
// Loading state
if (loading) {
return <Skeleton variant="rectangular" height={200} />;
}
// Error state with recovery
if (error) {
return (
<Alert severity="error">
Failed to load users: {error.message}
<Button onClick={() => window.location.reload()}>
Retry
</Button>
</Alert>
);
}
// Empty state
if (users.length === 0) {
return (
<EmptyState
icon={<PersonIcon />}
message="No users found"
action={<Button>Add User</Button>}
/>
);
}
// Success state
return <List>{users.map(user => <UserItem key={user.id} user={user} />)}</List>;
}
Feedback Pattern:
**Problem**: No loading indicator during data fetch, users see blank screen
**Fix**: Add loading state with Skeleton component
**Standard**: UX best practices - provide feedback during operations
**Impact**: Users confused, don't know if app is working, poor experience
Red Flags:
- No loading indicators for async operations
- Generic error messages ("Something went wrong")
- No empty states (blank screen when no data)
- No feedback after user actions (save, delete, etc.)
- Instant navigation without confirming unsaved changes
9. Form Handling & Validation
Check for:
- Controlled components: React state drives input values
- Form libraries: react-hook-form or Formik for complex forms
- Client-side validation: Immediate feedback, use Zod or Yup
- Error display: Show errors near fields, screen reader accessible
- Submit states: Disable button during submission
- Field labels: Every input has visible label
- Autocomplete attributes: For better UX and accessibility
Recommended Pattern:
// Good: react-hook-form with Zod validation
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const userSchema = z.object({
name: z.string().min(1, 'Name is required').max(100),
email: z.string().email('Invalid email address'),
age: z.number().min(18, 'Must be 18 or older'),
});
type UserFormData = z.infer<typeof userSchema>;
function UserForm() {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<UserFormData>({
resolver: zodResolver(userSchema),
});
const onSubmit = async (data: UserFormData) => {
await saveUser(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<TextField
{...register('name')}
label="Name"
error={!!errors.name}
helperText={errors.name?.message}
fullWidth
required
autoComplete="name"
/>
<TextField
{...register('email')}
label="Email"
type="email"
error={!!errors.email}
helperText={errors.email?.message}
fullWidth
required
autoComplete="email"
/>
<Button
type="submit"
disabled={isSubmitting}
fullWidth
variant="contained"
>
{isSubmitting ? 'Saving...' : 'Save'}
</Button>
</form>
);
}
Feedback Pattern:
**Problem**: Form validation only on submit, no field-level feedback
**Fix**: Use react-hook-form with inline validation
**Standard**: React forms best practices (react.dev)
**Benefit**: Better UX with immediate feedback, prevents submission errors
Red Flags:
- Uncontrolled components (no state)
- No validation or only server-side validation
- Generic error messages not tied to fields
- No disabled state during submission
- Missing autocomplete attributes
Official References:
- React Forms: https://react.dev/reference/react-dom/components/input
- react-hook-form: https://react-hook-form.com/
10. API Integration & Data Fetching
Check for:
- Data fetching libraries: TanStack Query (React Query) or SWR
- Error handling: Catch and display errors properly
- Loading states: Show during fetch
- Caching strategy: Avoid unnecessary refetches
- Retry logic: Automatically retry failed requests
- Optimistic updates: Update UI before server confirms
- TypeScript types: Type API responses properly
Recommended Pattern:
// Good: TanStack Query with TypeScript
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
interface User {
id: string;
name: string;
email: string;
}
// Query hook
function useUsers() {
return useQuery<User[], Error>({
queryKey: ['users'],
queryFn: async () => {
const response = await fetch('/api/users');
if (!response.ok) throw new Error('Failed to fetch users');
return response.json();
},
staleTime: 5 * 60 * 1000, // 5 minutes
retry: 3,
});
}
// Mutation hook with optimistic update
function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (user: User) => {
const response = await fetch(`/api/users/${user.id}`, {
method: 'PUT',
body: JSON.stringify(user),
});
if (!response.ok) throw new Error('Failed to update');
return response.json();
},
onMutate: async (updatedUser) => {
// Optimistic update
await queryClient.cancelQueries({ queryKey: ['users'] });
const previous = queryClient.getQueryData(['users']);
queryClient.setQueryData<User[]>(['users'], (old) =>
old?.map(u => u.id === updatedUser.id ? updatedUser : u)
);
return { previous };
},
onError: (err, variables, context) => {
// Rollback on error
if (context?.previous) {
queryClient.setQueryData(['users'], context.previous);
}
},
});
}
Feedback Pattern:
**Problem**: useEffect for data fetching, no caching, refetches on every render
**Fix**: Use TanStack Query for automatic caching and state management
**Standard**: React data fetching best practices
**Benefit**: Better performance, automatic retry, caching, less boilerplate
Red Flags:
- useEffect for all data fetching (should use library)
- No caching strategy
- No error retry logic
- Race conditions in data fetching
- Untyped API responses (using
any)
Official References:
- TanStack Query: https://tanstack.com/query/latest
- SWR: https://swr.vercel.app/
11. Testing Strategy
Check for:
- Component tests: React Testing Library for user-centric tests
- Accessibility tests: @testing-library/jest-dom matchers
- User interactions: fireEvent or userEvent for clicks, typing
- Async testing: waitFor for async state changes
- Mock API calls: msw (Mock Service Worker) for realistic API mocking
- Visual regression: Storybook + Chromatic for visual testing
- Coverage: Aim for critical user paths, not 100% line coverage
Recommended Pattern:
// Good: User-centric testing with React Testing Library
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import { UserForm } from './UserForm';
const server = setupServer(
rest.post('/api/users', (req, res, ctx) => {
return res(ctx.json({ id: '1', name: 'John' }));
})
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
describe('UserForm', () => {
it('submits form with valid data', async () => {
const user = userEvent.setup();
const onSuccess = jest.fn();
render(<UserForm onSuccess={onSuccess} />);
// Find inputs by label (accessible)
await user.type(screen.getByLabelText(/name/i), 'John Doe');
await user.type(screen.getByLabelText(/email/i), 'john@example.com');
// Submit form
await user.click(screen.getByRole('button', { name: /save/i }));
// Wait for success
await waitFor(() => {
expect(onSuccess).toHaveBeenCalled();
});
});
it('shows validation errors', async () => {
const user = userEvent.setup();
render(<UserForm />);
// Submit without filling
await user.click(screen.getByRole('button', { name: /save/i }));
// Check error messages
expect(screen.getByText(/name is required/i)).toBeInTheDocument();
});
it('is accessible', () => {
const { container } = render(<UserForm />);
// Check for form label associations
expect(screen.getByLabelText(/name/i)).toBeInTheDocument();
expect(screen.getByLabelText(/email/i)).toBeInTheDocument();
// Check button has accessible name
expect(screen.getByRole('button', { name: /save/i })).toBeInTheDocument();
});
});
Feedback Pattern:
**Problem**: Tests query by className instead of accessible roles/labels
**Fix**: Use screen.getByRole and screen.getByLabelText
**Standard**: React Testing Library best practices (testing-library.com)
**Benefit**: Tests reflect user experience, more resilient to refactoring
Red Flags:
- Testing implementation details (state, props)
- Querying by className or data-testid (instead of roles/labels)
- Not testing accessibility
- No async testing for data fetching
- Mock hell (mocking everything)
Official References:
- React Testing Library: https://testing-library.com/docs/react-testing-library/intro/
- Testing Best Practices: https://kentcdodds.com/blog/common-mistakes-with-react-testing-library
12. Security & Best Practices
Check for:
- XSS prevention: Never use dangerouslySetInnerHTML unless sanitized
- HTTPS only: API calls use HTTPS
- Auth tokens: Store in httpOnly cookies, not localStorage
- Input sanitization: Validate and sanitize user input
- CSP headers: Content Security Policy configured
- Dependency audits: npm audit regularly
- Environment variables: API keys not in client code
Recommended Pattern:
// Good: Safe HTML rendering with DOMPurify
import DOMPurify from 'dompurify';
function SafeHtmlDisplay({ html }: { html: string }) {
const sanitized = DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['p', 'br', 'strong', 'em'],
});
return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
}
// Good: Secure API call with error handling
async function fetchUserData(userId: string): Promise<User> {
const token = getAuthToken(); // From httpOnly cookie
const response = await fetch(`https://api.example.com/users/${userId}`, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error('Failed to fetch user');
}
return response.json();
}
Feedback Pattern:
**Problem**: Auth token stored in localStorage, vulnerable to XSS
**Fix**: Store token in httpOnly cookie, access via secure API
**Standard**: OWASP security best practices
**Impact**: Critical security vulnerability, token can be stolen via XSS
Red Flags:
- dangerouslySetInnerHTML without sanitization
- Sensitive data in localStorage
- API keys in client-side code
- HTTP instead of HTTPS
- No input validation
- Outdated dependencies with known vulnerabilities
Official References:
- OWASP Top 10: https://owasp.org/www-project-top-ten/
- React Security: https://react.dev/learn/manipulating-the-dom-with-refs#best-practices-for-dom-manipulation-with-refs
Review Process Workflow
Step 1: Gather Context
- Identify PR number and affected components
- Understand UX changes (screenshots, design specs)
- Note framework versions (React, TypeScript, MUI)
- Identify accessibility requirements
Step 2: Verify Standards
- Search React.dev for current patterns
- Check TypeScript handbook for type safety
- Review MUI documentation for component usage
- Verify WCAG compliance for accessibility
- Check MDN for web standards
Step 3: Analyze Code
Review systematically through 12 focus areas:
- Accessibility (WCAG)
- Component Architecture
- TypeScript Type Safety
- Performance
- Material-UI Best Practices
- State Management
- React Hooks
- UX Patterns
- Forms & Validation
- API Integration
- Testing
- Security
Step 4: Create Feedback File
- Create
PR-{PRNumber}-UX-Feedback.md - Categorize: [CRITICAL], [HIGH], [RECOMMEND]
- Include official doc references
- Add accessibility audit summary
- List positive observations
Step 5: Final Review
- Verify all accessibility issues flagged
- Ensure user impact clearly stated
- Check official doc references
- Validate code examples provided
Quick Reference: Issue Prioritization
[CRITICAL] (Must Fix Before Merge):
- Accessibility violations (keyboard nav, screen readers)
- Security vulnerabilities (XSS, exposed secrets)
- Performance issues causing UI freeze/crash
- Breaking changes to component APIs
- TypeScript any types in critical code
[HIGH] (Should Fix):
- Missing loading/error states
- Poor UX patterns (no feedback, confusing flows)
- Performance issues (unnecessary renders)
- Form validation gaps
- Missing tests for critical user paths
[RECOMMEND] (Consider):
- Component refactoring for reusability
- Additional accessibility improvements (AA → AAA)
- Performance optimizations (memoization)
- Better TypeScript types
- Additional test coverage
[LOW] (Nice to Have):
- Minor styling improvements
- Variable naming consistency
- Additional comments
- Refactoring non-critical code
UX-Specific Red Flags Checklist
Accessibility:
- Non-semantic HTML (div/span as buttons)
- Missing keyboard navigation
- No focus indicators
- Poor color contrast
- Missing alt text or ARIA labels
Performance:
- Large lists without virtualization
- No code splitting
- Unnecessary re-renders
- Expensive calculations without memoization
User Experience:
- No loading states
- Generic error messages
- No empty states
- Missing feedback after actions
- Confusing navigation
Type Safety:
- any types
- @ts-ignore comments
- Missing prop types
- Untyped API responses
Component Quality:
- God components (>300 lines)
- Prop drilling
- No error boundaries
- Duplicated logic
Final Notes
- Accessibility is non-negotiable - WCAG AA minimum
- User experience first - Code elegance is secondary
- Performance matters - Every millisecond counts
- Type safety protects users - Catch errors before users see them
- Test what users do - Not implementation details
- Reference official docs - Don't rely on memory
- Be constructive - Help the team improve UX skills
- Think mobile-first - Responsive design by default
This skill represents UX review patterns from production React applications, updated with latest React 19, TypeScript 5, Material-UI v5, and WCAG 2.1 standards.
Repository
