ThanhDev
  • Articles
  • Categories
  • Tags
  • About

ThanhDev

Frontend engineering notes on React, Next.js, TypeScript and software architecture.

  • Articles
  • Categories
  • Tags
  • About
  • GitHub
  • LinkedIn
  • Website
  • Email

© 2026 ThanhDev. Built with Next.js, Prisma and PostgreSQL.

  1. Home
  2. Articles
  3. Understanding React Rendering
React

Understanding React Rendering

A practical explanation of how React rendering actually works under the hood, and why most re-render bugs are really state placement bugs.

T

ThanhDev

Sep 5, 2026•2 min read•49 views

React rendering is one of the most misunderstood concepts in modern frontend development. Most performance advice you read online treats it as something to fight against. It isn't. Rendering is cheap; the expensive part is what you do during it.

What rendering actually means

Rendering is the process where React calls your component function to find out what the UI should look like. It does not mean touching the DOM.

There are three distinct phases:

  1. Render — React calls your component and builds a description of the UI.
  2. Reconcile — React diffs that description against the previous one.
  3. Commit — React applies the minimal set of DOM mutations.

Only the commit phase talks to the browser. A component that renders a hundred times but commits nothing is usually fine.

What triggers a render

A component re-renders when one of these happens:

  • Its own state changes.
  • Its parent re-renders.
  • A context it consumes changes value.

That second point is the one that surprises people. Props being "the same" does not stop a re-render — React has no way to know your component is pure unless you tell it.

tsx
function Parent() {
  const [count, setCount] = useState(0);
 
  return (
    <div>
      <button onClick={() => setCount((c) => c + 1)}>{count}</button>
      {/* Child re-renders on every click, even though it takes no props */}
      <Child />
    </div>
  );
}

The fix is usually structural

Before reaching for memo, move the state down to the component that actually uses it:

tsx
function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount((c) => c + 1)}>{count}</button>;
}
 
function Parent() {
  return (
    <div>
      <Counter />
      <Child />
    </div>
  );
}

Now Child never re-renders, and there is no memoization to keep correct.

Memoization is a cache. Every cache is a correctness risk plus a maintenance cost. Reach for structure first.

When memoization does help

Situation Use
Genuinely expensive computation useMemo
Stable identity for a dependency array useCallback
Large list where the parent updates often memo
Everything else Nothing

Measure with the React DevTools Profiler before and after. If you cannot see the difference in a flame chart, you have added complexity for nothing.


The short version: renders are not the problem. Rendering the wrong subtree is, and the cheapest fix is almost always moving state closer to where it is read.

  • #React
  • #Performance
Share
PreviousNext.js App Router: Server Components in PracticeNextNext.js Hydration

Related articles

  • React

    TanStack Query or Server Components?

    Both fetch data, but they solve different problems. A decision guide instead of a turf war.

    • #React
    • #TanStack Query
    • #Server Components
    Jul 30, 2026•2 min read•687 views
  • Next.js

    Next.js App Router: Server Components in Practice

    Where the server/client boundary actually belongs, and how to stop accidentally shipping your whole application to the browser.

    • #Next.js
    • #React
    • #Server Components
    Aug 30, 2026•2 min read•882 views
  • TypeScript

    TypeScript Discriminated Unions for Safer UI State

    Four booleans give you sixteen states, and twelve of them are nonsense. Model the four you actually have instead.

    • #TypeScript
    • #React
    Aug 23, 2026•2 min read•543 views