React Correctness Rules
Rule
Scope: React and Next.js apps. Pass to daniel-product-engineer, lee-nextjs-engineer, security-engineer, and senior-engineer during audits.
Keys & Rendering
- MUST: Use stable unique IDs as
key, not array index — index keys cause state bugs on reorder/insert/delete- Exception: Static lists generated from
Array.from()placeholders where items never reorder
- Exception: Static lists generated from
- MUST NOT: Use
&&with numbers for conditional rendering —{count && }renders0- FIX:
{count > 0 ? : null}
- FIX:
State & Effects
- MUST NOT: Use
useEffectthat only callssetStatederived from deps — replace withuseMemo:// Wrong — unnecessary effect + re-render const [fullName, setFullName] = useState(''); useEffect(() => setFullName(`${first} ${last}`), [first, last]); // Right — derived state const fullName = useMemo(() => `${first} ${last}`, [first, last]); - SHOULD: Consolidate 3+ related
useStatecalls intouseReduceror a single state object - SHOULD: Batch multiple
setStatecalls in one handler into a single update (React 18+ batches automatically in most cases, but verify in async callbacks) - MUST NOT: Fetch data in
useEffect— use React Query, SWR, or server components instead
Naming & Structure
- SHOULD: Name event handlers by what they do, not what they handle:
- Wrong:
handleClick,handleChange - Right:
handleSubmitOrder,handleUpdateQuantity
- Wrong:
- SHOULD: Break components over 250 lines into smaller focused components
- MUST NOT: Use inline render functions in JSX (
{renderItems()}) — extract to a named component- Inline render functions don't get their own reconciliation boundary, causing unnecessary re-renders of siblings
Security
- MUST NOT: Use
eval(),new Function(), or string-argumentsetTimeout/setInterval— code injection risk - MUST NOT: Hardcode secrets in source — detected by variable name patterns:
API_KEY,SECRET,TOKEN,PASSWORD,PRIVATE_KEY- FIX: Use environment variables via
process.envor.envfiles
- FIX: Use environment variables via
- MUST: Flag every
dangerouslySetInnerHTMLusage for security review — ensure input is sanitized (DOMPurify or equivalent)
Server Components & Actions (Next.js)
- MUST NOT: Make client components async —
"use client"components cannot be async (they return a Promise, not JSX) - MUST: Add auth check at the top of every server action —
auth(),getSession(), or equivalent before any data mutation - SHOULD: Use
next/imageinstead of<img>— provides automatic optimization, lazy loading, and layout shift prevention - SHOULD: Use
next/linkinstead of<a>for internal navigation — enables client-side transitions and prefetching - SHOULD: Use
next/fontinstead of<link>Google Font tags — eliminates layout shift from font loading
Error Handling
- MUST: Wrap async operations in try/catch with meaningful error handling — no empty catch blocks
- SHOULD: Add error boundaries around feature sections that can fail independently
- MUST NOT: Silently swallow errors — at minimum log them; prefer surfacing to the user