Audit the React 19 configuration and usage patterns for this Vite SPA project. Check for correctness, performance, and modern best practices.
Target: $ARGUMENTS (default: full audit — scan all src/**/*.{tsx,ts})
Pre-flight
- Read
src/main.tsx,src/App.tsx,package.json, andtsconfig.json. - Glob
src/**/*.tsxto inventory all components. - Grep for key patterns:
useEffect,useState,createContext,React.lazy,Suspense,ErrorBoundary. - Check React and React DOM versions in
package.json.
Audit Checklist
1. ENTRY POINT & ROOT SETUP
-
createRootused (not deprecatedReactDOM.render) -
<StrictMode>wrapping the app (catches bugs in development) - Root element exists in
index.htmlwith matching ID - No synchronous heavy work in
main.tsx(defer to lazy loading) - Error boundary wraps the root or top-level routes
- If using Sentry:
Sentry.init()called beforecreateRoot
2. ROUTING (react-router-dom v7)
- Using
createBrowserRouteror<BrowserRouter>(not<HashRouter>for Vercel deployment) - Routes use lazy loading via
React.lazy()for code splitting - 404/catch-all route defined
- Route layout components avoid unnecessary re-renders
-
useNavigateused instead of<Navigate>for programmatic navigation - No
window.locationfor in-app navigation (breaks SPA) - Vercel SPA rewrite in place to support direct URL access
3. STATE MANAGEMENT
-
useStatefor simple local state (not over-engineered with reducers) -
useReduceronly for complex state with multiple sub-values or next-state-depends-on-previous - Context not overused as global state (causes unnecessary re-renders)
- If using Context: value is memoized with
useMemoto prevent re-renders - No prop drilling deeper than 3 levels without Context or composition
- State lifted to lowest common ancestor, not to root
- No derived state stored in
useState— compute inline oruseMemo
4. EFFECTS & DATA FETCHING
-
useEffectnot used for data that can be computed from props/state -
useEffectcleanup functions present for subscriptions, timers, AbortControllers - No
useEffectwith empty deps just to run code once — consider event handlers instead - Data fetching uses AbortController for race condition prevention
- No
useEffect→setStatepatterns that cause render waterfalls -
useLayoutEffectonly for DOM measurement (not general side effects) - Fetch calls go through hooks (not raw
fetch()in components)
5. PERFORMANCE PATTERNS
-
React.lazy()+<Suspense>for route-level code splitting - Heavy components wrapped in
Suspensewith meaningful fallbacks -
useMemofor expensive computations (not for simple values) -
useCallbackfor functions passed to memoized children - No premature optimization (don't memo everything — profile first)
- Lists use stable
keyprops (not array index for dynamic lists) - Large lists use virtualization (
react-windowor similar) if > 100 items - Images use
loading="lazy"for below-fold content -
useTransitionfor non-urgent state updates (search, filtering) -
useDeferredValuefor expensive re-renders triggered by fast-changing values - React Compiler considered (auto-memoization) — check if
babel-plugin-react-compileris beneficial
6. COMPONENT PATTERNS
- Function components only (no class components in new code)
- Components are single-responsibility (not doing too many things)
- Custom hooks extract reusable logic from components
- No inline function definitions in JSX that create new references each render (when passed to memoized children)
- Conditional rendering uses early returns, not nested ternaries
-
childrenprop used for composition over configuration -
forwardRefused when exposing DOM refs (or React 19's ref-as-prop) - No
dangerouslySetInnerHTMLwithout sanitization
7. ERROR HANDLING
- Error boundary component exists and wraps routes/major sections
- Error boundaries show user-friendly fallback UI (not blank screen)
- Error boundaries report to Sentry or logging service
- Async errors caught in data fetching hooks (not just render errors)
- Network errors handled with retry/fallback UI
- Loading states shown during data fetching (
<Suspense>or conditional)
8. TYPESCRIPT & TYPES
- Components typed with explicit props interfaces (not
any) - Event handlers properly typed (
React.MouseEvent,React.ChangeEvent, etc.) -
useState<Type>()generic used when type can't be inferred - No
@ts-ignoreor@ts-expect-errorwithout explanation - Custom hooks return properly typed values
- No
astype assertions where type guards would be safer
9. REACT 19 FEATURES
- Using React 19+ (check
package.jsonversion) -
use()hook considered for reading promises/context in render -
useActionStateconsidered for form actions with pending state -
useOptimisticconsidered for optimistic UI updates -
refas prop works withoutforwardRefin React 19 — check ifforwardRefcan be removed -
<form action={}>considered for progressive enhancement - Document metadata (
<title>,<meta>) can be rendered directly in components (React 19)
10. ACCESSIBILITY (a11y)
- Interactive elements use semantic HTML (
<button>,<a>,<input>) - No
<div onClick>withoutrole="button",tabIndex, and keyboard handler - Form inputs have associated
<label>elements -
aria-labelon icon-only buttons - Focus management after route changes
-
useId()for generating unique IDs for accessibility attributes - Skip-to-content link present
- Color contrast meets WCAG AA (4.5:1 normal text, 3:1 large text)
Output Format
## React Audit Report
### Score: X/10 dimensions passing
### Critical Issues
1. [DIMENSION] — [severity: CRITICAL/HIGH/MEDIUM/LOW] — description — file:line
Current: `current code`
Fix: specific fix with code
### Warnings
1. [DIMENSION] — description — file:line
Recommendation: what to change
### Optimizations
1. [DIMENSION] — description — file:line
Benefit: expected improvement
### Passing
- [DIMENSION] — what's working well
### Key Recommendations
(Top 3 highest-impact changes to make)