# How React Compiler Memoization Works in the Instatic Admin App

> Discover how React Compiler memoization optimizes Instatic's admin app automatically. Eliminate manual useMemo, useCallback, and React.memo with this powerful tool.

- Repository: [CoreBunch/Instatic](https://github.com/CoreBunch/Instatic)
- Tags: internals
- Published: 2026-07-30

---

**React Compiler automatically memoizes components in Instatic's admin app, making manual `useMemo`, `useCallback`, and `React.memo` unnecessary except for three specific exceptions.**

Instatic by CoreBunch leverages the React Compiler to eliminate manual optimization clutter across its admin interface. The compiler runs in **infer mode**, automatically inserting memoization caches for every component and hook it recognizes. This approach ensures peak performance without requiring developers to manually wrap functions or components in memoization logic.

## Compiler Configuration and Infer Mode

The React Compiler is enabled globally via the Vite configuration in [`vite.config.ts`](https://github.com/CoreBunch/Instatic/blob/main/vite.config.ts) (lines 39‑45). According to the source code, the compiler automatically constructs a memo-cache for every function it successfully analyzes, eliminating the need for handwritten memoization patterns.

The project specifically configures the compiler to run in **infer mode**, which proved essential for compatibility with Instatic's state management. In this mode, the compiler works cleanly with the immutable snapshots produced by **Mutative**, avoiding the "cannot perform 'get' on a revoked proxy" errors that previously occurred (lines 48‑52). This integration is critical because the admin UI relies heavily on Zustand stores processed through Mutative for state updates.

## The Three Exceptions to Manual Memoization

Despite global auto-memoization, Instatic enforces a strict policy allowing manual memoization only in three sanctioned scenarios. All other instances are flagged as drift and must be removed.

### Exception 1: Hook Dependency Arrays

When a function serves as a dependency for `useEffect`, `useMemo`, or `useCallback`, you must wrap it in `useCallback` to maintain stable identity. The static `react-hooks/exhaustive-deps` ESLint rule cannot see the compiler's runtime memoization, so explicit wrapping is required to satisfy the linter.

```tsx
// ✅ Correct: useCallback kept for dependency array stability
const handlePointerDown = useCallback(
  (side: 'left' | 'right') => (event: ReactPointerEvent<HTMLDivElement>) => {
    // …implementation…
  },
  [effectiveWidth, activeBreakpoint],
);
// File: src/admin/pages/site/canvas/CanvasLiveSurface.tsx (lines 14‑25)

```

### Exception 2: Hot List Rendering with React.memo

Components rendered inside large lists or recursive structures (such as node renderers) may use `React.memo` to prevent O(N) re-renders. This exception applies only to components that would otherwise cause significant performance bottlenecks during list updates.

```tsx
// ✅ Allowed exception: React.memo on a hot list renderer
export const NodeRenderer = React.memo(function NodeRenderer({ node }) {
  // …render logic…
});
// Typical file: src/admin/pages/site/canvas/NodeRenderer.tsx

```

### Exception 3: Compiler Escape Hatches and Ref Access

Functions that access refs during render or utilize patterns the compiler cannot analyze require either `useCallback` or the `"use no memo"` directive. This includes complex closures that trigger `eslint-plugin-react-compiler` warnings or cases requiring `/* eslint-disable react-compiler/react-compiler */` comments.

```tsx
// ✅ Escape-hatch: compiler cannot compile this function
/* eslint-disable react-compiler/react-compiler */
function specialCase() {
  // complex closure or ref access during render
}
/* eslint-enable react-compiler/react-compiler */

```

## Enforcement Through Linting and CI

Instatic enforces these rules via **eslint-plugin-react-compiler** combined with **eslint-plugin-react-hooks** (configured in [`eslint.config.js`](https://github.com/CoreBunch/Instatic/blob/main/eslint.config.js)). When `bun run lint` executes, any manual memoization found outside the three exceptions is flagged as policy drift. The configuration distinguishes legitimate exceptions from noise, ensuring only necessary memoization remains in the bundle.

The **react-doctor** tool also flags manual memoization but only emits warnings because it cannot recognize the three semantic exceptions. This dual-layer enforcement—strict ESLint rules in CI plus advisory warnings from react-doctor—maintains code consistency across the entire admin codebase.

## Example Patterns in the Codebase

The [`src/ui/components/ContextMenu/ContextMenuSubmenu.tsx`](https://github.com/CoreBunch/Instatic/blob/main/src/ui/components/ContextMenu/ContextMenuSubmenu.tsx) file demonstrates the standard Instatic pattern: it contains no manual memoization, allowing the compiler to handle all optimization transparently. Conversely, [`CanvasLiveSurface.tsx`](https://github.com/CoreBunch/Instatic/blob/main/CanvasLiveSurface.tsx) shows the approved exception pattern where `useCallback` preserves function identity specifically for an `exhaustive-deps` rule requirement.

Removing unnecessary `useMemo` calls reduces bundle size and eliminates duplicated logic while the compiler guarantees that components only re-render when props or state actually change. The full policy is documented in [`docs/reference/react-compiler.md`](https://github.com/CoreBunch/Instatic/blob/main/docs/reference/react-compiler.md) and enforced automatically in CI pipelines.

## Summary

- **Auto-memoization**: The React Compiler in [`vite.config.ts`](https://github.com/CoreBunch/Instatic/blob/main/vite.config.ts) (infer mode) automatically caches every eligible component and hook.
- **Three exceptions only**: Manual memoization is permitted for hook dependencies, hot list renderers with `React.memo`, and compiler escape-hatches/ref cases.
- **Lint enforcement**: `eslint-plugin-react-compiler` and `eslint-plugin-react-hooks` flag violations, while `react-doctor` provides secondary warnings.
- **Mutative compatibility**: Infer mode prevents proxy revocation errors when working with Zustand and Mutative immutable updates.
- **Bundle impact**: Eliminating manual memoization reduces code size and maintenance overhead without sacrificing performance.

## Frequently Asked Questions

### When should I use `useCallback` in the Instatic admin app?

Use `useCallback` only when the function appears in a dependency array for `useEffect`, `useMemo`, or `useCallback` itself, as the static ESLint rule cannot detect the compiler's automatic memoization. This ensures the `exhaustive-deps` rule receives a stable function identity.

### Why do some components still use `React.memo`?

Components that render inside hot lists or recursive structures (like node renderers) keep `React.memo` to prevent O(N) re-render cascades. Each instance requires a one-line justification comment explaining the performance necessity.

### How do I disable the React Compiler for a specific function?

Add the `"use no memo"` directive at the top of the function and wrap it with `/* eslint-disable react-compiler/react-compiler */` comments. This escape-hatch is reserved for complex closures or ref-access patterns that the compiler cannot analyze.

### What happens if I commit manual memoization that violates the policy?

CI runs `bun run lint` using `eslint-plugin-react-compiler`, which will fail the build if it detects manual memoization outside the three sanctioned exceptions. The code must be cleaned up before merging, ensuring consistent optimization across the Instatic admin codebase.