Memoization cheatsheet.

The default: don’t memoize

Default React behavior — every state change re-renders subtree. That’s fine 95% of the time. Don’t optimize prematurely.

Reach for memoization when you’ve measured a slow render and know why.

React Compiler (2025+)

The React Compiler auto-memoizes for you. If you’re using it, skip explicit memo/useMemo/useCallback.

// .babelrc / next.config.js
{
  "plugins": ["babel-plugin-react-compiler"]
}

Compiler reads your code and inserts memoization where beneficial.

React.memo

const Item = React.memo(function Item({ name, count }: Props) {
  return <li>{name}: {count}</li>;
});

Skips re-render if props are shallow-equal to previous.

Custom compare:

const Item = React.memo(Item, (prev, next) => prev.id === next.id);

useMemo

const sorted = useMemo(() => items.slice().sort(cmp), [items]);

Cache expensive computation. Trade memory for CPU.

// Cache object identity (referenced as dep elsewhere)
const config = useMemo(() => ({ apiUrl, timeout }), [apiUrl, timeout]);

useCallback

const onClick = useCallback((id: number) => doIt(id), [doIt]);

Cache function identity. Only meaningful if passed to:

  • A memoized component (React.memo)
  • An effect/hook deps array

Otherwise it’s noise.

When memo helps

  • Child component is expensive to render.
  • Child is in a tight loop (large list).
  • Function or object prop changes identity unnecessarily.

When it doesn’t:

  • Child is cheap (most components are).
  • Props change every render anyway.
  • New object/array literal as prop — memo can’t help.

Measuring

import { Profiler } from "react";

<Profiler id="List" onRender={(id, phase, dur) => console.log(id, phase, dur)}>
  <List />
</Profiler>

Or React DevTools Profiler tab — record, see hot components.

Lifting work out of render

// BAD: expensive in render
function List({ items }: { items: Item[] }) {
  const sorted = items.slice().sort(cmp);    // every render
  return <ul>{sorted.map(...)}</ul>;
}

// GOOD: useMemo
const sorted = useMemo(() => items.slice().sort(cmp), [items]);

List virtualization

For huge lists, render only visible:

import { useVirtualizer } from "@tanstack/react-virtual";

const v = useVirtualizer({
  count: items.length,
  getScrollElement: () => parentRef.current,
  estimateSize: () => 40,
});

return (
  <div ref={parentRef} style={{ height: 400, overflow: "auto" }}>
    <div style={{ height: v.getTotalSize() }}>
      {v.getVirtualItems().map((row) => (
        <div key={row.key} style={{ position: "absolute", top: row.start }}>
          {items[row.index].name}
        </div>
      ))}
    </div>
  </div>
);

10k items, only ~20 DOM nodes.

Keys and stable identity

Reused identity matters:

{items.map((it) => (
  <Item key={it.id} item={it} />     // stable key → React reuses
))}

Reorder with index keys destroys child state.

Avoid creating arrays/objects inline

// BAD: new array every render
<Child items={data.filter(p)} />

// BETTER
const filtered = useMemo(() => data.filter(p), [data]);
<Child items={filtered} />

If Child is React.memo, this matters; otherwise it’s fine.

Splitting components

Sometimes the fix is structural:

function Parent() {
  const [count, setCount] = useState(0);
  return (
    <>
      <Counter count={count} setCount={setCount} />
      <ExpensiveTree />        // doesn't depend on count
    </>
  );
}

If ExpensiveTree doesn’t depend on count, move state down:

function Parent() {
  return (
    <>
      <Counter />              // owns its own count
      <ExpensiveTree />
    </>
  );
}

Context value memoization

// Provider:
const value = useMemo(() => ({ user, login, logout }), [user]);

Or split context (see Cheatsheet 05).

Common mistakes

  • Memoizing everything “just in case” — overhead may exceed savings.
  • useCallback for handlers used only in JSX — adds work, no benefit.
  • React.memo + always-changing prop — never bails out.
  • Forgetting useMemo dep — stale data.
  • Wrapping cheap components in memo — slower than not.

Read this next

If you want my virtualization + heavy-list patterns, they’re at rajpoot.dev .


Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .