React Compiler Impact on Memoization Patterns in Instatic: A Complete Guide
The React Compiler eliminates the need for manual memoization patterns like useMemo, useCallback, and React.memo throughout Instatic by automatically memoizing components and hooks at build time, with only three specific exceptions allowed.
Instatic is a React codebase that fully embraces the React Compiler to handle performance optimizations automatically. By leveraging build-time analysis via reactCompilerPreset() in vite.config.ts, the project eliminates traditional manual memoization boilerplate while enforcing strict rules about when developers may still use useCallback or React.memo. Understanding these React Compiler memoization patterns in Instatic helps contributors write cleaner, more maintainable code without sacrificing runtime performance.
How the React Compiler Automates Memoization in Instatic
The React Compiler performs static analysis during the build process to automatically memoize every component and hook. In vite.config.ts, the compiler is enabled via reactCompilerPreset(), which transforms your code to include stable identities for values, functions, and components without manual intervention.
Because the compiler generates these optimizations automatically, any additional hand-written memoization is considered noise and provides no additional performance benefit. The project configures react-doctor.config.json to downgrade compiler-related warnings to advisory status, acknowledging that the build-time analysis handles memoization concerns that developers traditionally managed manually.
The "No Manual Memoization" Rule
Instatic enforces a strict prohibition against useMemo, useCallback, and React.memo() throughout the codebase. According to the reference documentation in docs/reference/react-compiler.md, manual memoization patterns are unnecessary because the compiler already generates stable references for functions and values.
This rule is actively enforced through ESLint and documented in code comments. For example, in src/admin/spotlight/SpotlightRoot.tsx, you will find explicit comments prohibiting useMemo where developers might traditionally reach for it, noting that the React Compiler handles optimization automatically even in complex factory patterns.
The Three Exceptions Where Manual Memoization Is Allowed
Despite the general prohibition, the Instatic codebase recognizes three legitimate scenarios where manual memoization remains necessary.
Exception 1: Hook Dependency Arrays
When a function is used inside a hook's dependency array, you must wrap it in useCallback to satisfy the react-hooks/exhaustive-deps ESLint rule. This ensures the function maintains a stable identity across renders, preventing infinite loops or missed effect executions.
export function SearchBox() {
const [query, setQuery] = useState('');
// `handleChange` is used in a `useEffect` deps array → must be stable.
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setQuery(e.target.value);
}, []); // ← exception 1
useEffect(() => {
// Do something with `query` when it changes.
}, [query, handleChange]);
return <input value={query} onChange={handleChange} />;
}
Exception 2: Hot-List Components
Components that render recursively or handle high-frequency updates—such as canvas renderers or large tree views—may benefit from an explicit React.memo bail-out. This manual memoization prevents unnecessary reconciliation in hot paths where the compiler's automatic memoization might not provide sufficient granularity.
// In a tree-renderer where each node is rendered O(N) times.
const NodeRenderer = memo(function NodeRenderer({ node }: { node: TreeNode }) {
// Rendering logic …
return <div>{node.label}</div>;
}); // ← exception 2
Exception 3: Compiler Escape Hatches
When the React Compiler encounters a complex closure or pattern it cannot compile, you may use manual useCallback with an ESLint disable comment. This escape hatch is reserved for scenarios where the compiler's static analysis fails to determine dependency stability.
// The compiler cannot compile this complex closure.
export const complexHandler = useCallback(() => {
// ...complex logic...
}, []); // eslint-disable-next-line react-compiler/react-compiler
Code Examples: Correct vs. Incorrect Patterns
The following examples demonstrate the memoization patterns enforced throughout the Instatic codebase.
Correct: Plain function without memoization
export function PageHeader({ title }: { title: string }) {
// Plain function – the React Compiler will memoize it.
return <h1>{title}</h1>;
}
Incorrect: Unnecessary manual memoization
const expensiveValue = useMemo(() => computeExpensive(), []); // ❌ drift
When exceptions apply, always include a brief comment explaining why manual memoization is necessary, satisfying the lint rules without introducing redundant abstraction layers.
Lint Gates and Enforcement
Instatic enforces these memoization rules through a dual ESLint configuration defined in eslint.config.js. The setup combines eslint-plugin-react-compiler with eslint-plugin-react-hooks to run checks during bun run lint.
The compiler plugin identifies code that violates the "no manual memoization" policy, while the hooks plugin ensures that the three exceptions—particularly dependency array stability—are handled correctly. Together, these tools maintain code quality and prevent performance anti-patterns from entering the codebase.
Summary
- Automatic memoization: The React Compiler handles all memoization at build time via
reactCompilerPreset()invite.config.ts, makinguseMemo,useCallback, andReact.memounnecessary in most cases. - Strict prohibition: Manual memoization is considered noise and is actively removed; reference
docs/reference/react-compiler.mdfor the complete policy. - Three exceptions: Only use manual memoization for hook dependency arrays, hot-list recursive components, or compiler escape hatches requiring ESLint disable comments.
- Enforcement:
eslint-plugin-react-compilerandeslint-plugin-react-hooksrun onbun run lintto maintain these standards. - Real-world examples: Files like
src/admin/spotlight/SpotlightRoot.tsxdemonstrate the practical application of these rules with inline documentation.
Frequently Asked Questions
Why does Instatic prohibit useMemo and useCallback?
The React Compiler automatically generates stable identities for values and functions during the build process. Manual memoization duplicates this work without providing additional benefits, creating unnecessary abstraction layers and maintenance overhead. As implemented in CoreBunch/Instatic, the compiler's build-time analysis makes hand-written memoization redundant.
When should I use useCallback in Instatic?
You should only use useCallback when a function is referenced in a hook's dependency array to satisfy react-hooks/exhaustive-deps, or when the React Compiler cannot compile a specific function and you need to provide an explicit stable reference. Always add a comment explaining the exception when you use this pattern.
How do I handle functions that the React Compiler cannot compile?
When the compiler fails to analyze a complex closure, wrap the function in useCallback with an empty dependency array and add // eslint-disable-next-line react-compiler/react-compiler on the preceding line. This escape hatch is reserved for edge cases where static analysis fails, and should be used sparingly according to the guidelines in docs/reference/react-compiler.md.
What happens if I accidentally add manual memoization?
The ESLint configuration will flag unnecessary useMemo, useCallback, or React.memo usage during bun run lint. These violations must be resolved before merging, either by removing the manual memoization or documenting the specific exception category that applies to your use case. The project treats redundant memoization as technical debt that complicates the codebase without improving performance.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →