Skip to content
fearchitect
Performance Engineering

Render Performance

Skip renders, defer slow work, and virtualize long lists.

By Abas TurabliReviewed

Summary

Unnecessary re-renders, blocking updates, and oversized DOM trees are the three main causes of sluggish React UIs. React.memo, useMemo, and useCallback let you skip work; useTransition and useDeferredValue keep input responsive while slow updates run in the background; TanStack Virtual's useVirtualizer renders only visible rows.

Jump to the interview angle

Where render cost comes from

  • Too many components re-render on a single interaction.
  • A long task blocks the main thread past the 200ms INP budget.
  • A large list renders every row at once instead of only the visible ones.

useTransition — keep input responsive

Mark the expensive state update as a transition. The input stays responsive while the heavy list re-renders in the background, and React interrupts that work if the user types again.

Non-blocking filter with useTransitiontsx
"use client";
import { useState, useTransition } from "react";

export function Filter({ rows }: { rows: string[] }) {
  const [query, setQuery] = useState("");
  const [shown, setShown] = useState(rows);
  const [isPending, startTransition] = useTransition();

  function onChange(e: React.ChangeEvent<HTMLInputElement>) {
    const q = e.target.value;
    setQuery(q); // urgent: keeps the input responsive
    startTransition(() => {
      // non-urgent: re-filter 10,000 rows without blocking keystrokes
      setShown(rows.filter((r) => r.includes(q)));
    });
  }

  return (
    <>
      <input value={query} onChange={onChange} />
      <List rows={shown} dim={isPending} />
    </>
  );
}

startTransition flags the setShown update as non-urgent, so typing never stutters. isPending drives a subtle dimmed state instead of a blocking spinner.

Profile before you memoize

Measure with the React DevTools Profiler first. Don't scatter useMemo/useCallback by reflex — the React Compiler (1.0, October 2025) auto-inserts memoization for Rules-of-React-compliant code.

Tradeoffs

Pros

  • useTransition / useDeferredValue keep interactions responsive under heavy updates.
  • Virtualization renders only visible rows — near-constant cost at any list size.
  • The React Compiler removes most manual memoization.

Cons

  • Over-memoization adds equality-check cost and clutter.
  • Virtualization complicates focus, scroll restoration, and accessibility.
  • Concurrent features surprise code that assumes synchronous renders.

Interview angle

Interviewers probe which tool fits which problem. Distinguish memoization (skip the render), transitions (defer the render), and virtualization (shrink the DOM). Name INP as the metric transitions target.

Soundbite: "Profile first — most renders are cheap. For slow ones: memo to skip, useTransition to defer, virtual to shrink the DOM."

Key terms

useTransition
Hook that marks a state update as interruptible so urgent events (typing) run first.
useDeferredValue
Hook that exposes a lagging value; background re-render is restartable on new input.
useVirtualizer
TanStack Virtual hook that renders only visible list items, keeping DOM size constant.
INP
Interaction to Next Paint — Core Web Vital measuring input-to-visual-update latency, budget 200 ms.
React Compiler
Build-time tool (stable v1.0, Oct 2025) that auto-inserts memoization for compliant code.

Further reading

Search fearchitect

Jump to a topic, mode, or action.