Claude Code Plugins

Community-maintained marketplace

Feedback

react-ux-code-reviewer

@ranjanpoudel1234/ai-tools
1
0

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.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

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 WebSearch or WebFetch for 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 WebSearch for 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 WebSearch or WebFetch for 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 WebSearch for w3.org/WAI

5. Check MDN Web Docs

  • Verify HTML5 semantic elements
  • Check CSS best practices
  • Review Web API usage
  • Validate browser compatibility
  • Use WebSearch for 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:

  1. Specific Official Resource: Link to the exact React.dev, TypeScript, MUI, WCAG, or MDN article
  2. Direct URL: Include the full URL in your feedback
  3. Code Examples: When available, reference official code samples from the documentation
  4. 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 documentation
  • WebFetch - Get full article content from official sites
  • context7 MCP - Get up-to-date React, TypeScript, Next.js documentation

Official Resource URLs:

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:


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:


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:


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:


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:


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:


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:


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:


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:


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:


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:


Review Process Workflow

Step 1: Gather Context

  1. Identify PR number and affected components
  2. Understand UX changes (screenshots, design specs)
  3. Note framework versions (React, TypeScript, MUI)
  4. Identify accessibility requirements

Step 2: Verify Standards

  1. Search React.dev for current patterns
  2. Check TypeScript handbook for type safety
  3. Review MUI documentation for component usage
  4. Verify WCAG compliance for accessibility
  5. 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

  1. Create PR-{PRNumber}-UX-Feedback.md
  2. Categorize: [CRITICAL], [HIGH], [RECOMMEND]
  3. Include official doc references
  4. Add accessibility audit summary
  5. 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.