Skip to solution
easyFrontend

What is the Virtual DOM in React?

404 views
01

Understand the problem

Question presented to candidate: "What is the Virtual DOM, and why does React use one?"

What a strong answer should cover:

  • A lightweight in-memory representation of the UI, made of plain objects (React elements). It is not a browser feature and not a copy of the DOM.
  • On each render React builds a new tree, diffs it against the previous one (reconciliation), and applies only the differences to the real DOM.
  • Why: real DOM operations are expensive relative to object comparison, and touching it triggers layout and paint.
  • The honest framing: it is not faster than optimal hand-written DOM code. Diffing is extra work vanilla code skips. What it buys is making the declarative model fast enough to be practical.
  • The diffing heuristics that make it O(n): different element types mean discard and rebuild; keys identify children across renders.
  • Consequence of the type heuristic: changing an element's type unmounts the subtree and destroys its state.
  • Fiber is the implementation that made this interruptible; the Virtual DOM is the data model, Fiber is the scheduler.
  • Related but distinct: the shadow DOM is a browser encapsulation feature with no connection to this.

Clarifying questions expected:

  • "Do you want the concept, or the diffing heuristics?"

Code / implementation expected: Optional. Showing that an element is a plain object makes the point faster than prose.

virtual domperformancereconciliationdom
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Anyone preparing for a React interview — assumes basic components. Difficulty: Easy

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. The reconciliation behaviour in section 5 was produced by actually swapping an eleme

Solution ready — 2 min read

Classified // press E to declassify

04

Explore the playground snippets

Elements are plain objects, and a type change destroys the subtree
Run Playground
import { useState } from "react";

function Counter() {
  const [n, setN] = useState(0);
  return (
    <button onClick={() => setN((v) => v + 1)}>
      clicked {n} times
    </button>
  );
}

// Two wrappers that render almost identical output — but they are DIFFERENT
// element types, which is all reconciliation cares about.
function DivWrapper({ children }) {
  return <div style={{ padding: 10, border: "2px solid #4f46e5", borderRadius: 8 }}>{children}</div>;
}
function SectionWrapper({ children }) {
  return <section style={{ padding: 10, border: "2px solid #16a34a", borderRadius: 8 }}>{children}</section>;
}

export default function App() {
  const [asSection, setAsSection] = useState(false);
  const Wrapper = asSection ? SectionWrapper : DivWrapper;

  // The element is just data — inspect it.
  const element = <Counter />;
  const shape = {
    typeofElement: typeof element,
    typeIsTheFunction: element.type === Counter,
    keys: Object.keys(element).join(", "),
    frozen: Object.isFrozen(element),
  };

  return (
    <div style={{ padding: 24, fontFamily: "system-ui", lineHeight: 1.6, maxWidth: 480 }}>
      <h4 style={{ marginTop: 0 }}>An element is a plain object</h4>
      <pre style={{ background: "#f6f6f6", padding: 10, borderRadius: 6, fontSize: 12 }}>
        {JSON.stringify(shape, null, 2)}
      </pre>

      <h4>Changing the wrapper TYPE destroys the child state</h4>
      <Wrapper>
        <Counter />
      </Wrapper>

      <p style={{ marginTop: 10 }}>
        <button onClick={() => setAsSection((s) => !s)}>
          swap wrapper to a &lt;{asSection ? "div" : "section"}&gt;
        </button>
      </p>

      <p style={{ color: "#666", fontSize: 13 }}>
        Click the counter a few times, then swap the wrapper. The count resets to
        zero — React saw a different element type, so it discarded the whole
        subtree and mounted a fresh one rather than trying to match it up.
      </p>
    </div>
  );
}
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 17 of 119 decoded in the React.js track. One more won't hurt.

Back to track