The Compiler covers 95% of the memoization you used to write by hand. The remaining 5% is worth knowing — that's where careful application of useMemo, useCallback, and React.memo still helps.
Case 1 — referential identity for non-React APIs
You pass a callback to a Web Component (or Three.js, or a Web Worker bridge) that compares props by identity. The Compiler memoizes for React's reconciler — but the external API has its own equality check. Manual useCallback guarantees the reference is stable in a way the external library expects.
Case 2 — expensive pure computations
The Compiler memoizes values whose inputs are React-tracked (props, state). For a value computed from external/imperative sources (a DOM measurement, a Date.now(), an imported lookup table), wrap it in useMemo manually if the computation is genuinely expensive. The Compiler isn't conservative; it's just narrow.
Case 3 — React.memo on list items
A long list where each row is moderately expensive: wrapping Row in React.memo means individual rows don't re-render when sibling rows update. The Compiler can sometimes infer this, but explicit React.memo is still the clearest signal — and works regardless of Compiler enablement.
Case 4 — Compiler isn't enabled
Not every project has the Compiler turned on. If you're contributing to a codebase that doesn't, manual memoization is the only tool you have. Match the codebase's style.