The Problem React Was Built to Solve
The DOM was state storage before React. jQuery helped but did not fix the root problem. React moved state into JavaScript and the entire mental model shifted.
I used to think React was just jQuery with better syntax. Both manipulate the DOM. Both are JavaScript libraries. What is the actual difference?
The difference is not the syntax.
It is where state lives.
The jQuery Era
Before frameworks, building an interactive page meant reaching directly into the DOM to read and change values. You have a counter. A user clicks increment. You do something like:
const el = document.getElementById('count');
el.textContent = Number(el.textContent) + 1;The DOM is your database. You read from it. You write to it.
jQuery made this cross-browser and less verbose. For the era it was built for — interactive sprinkles on top of server-rendered pages — it was the right tool. A form that shows an error message. A tab switcher. A dropdown that opens on hover. jQuery handled all of that cleanly.
What Breaks When the App Grows
The problem shows up when the same piece of data is needed in multiple places.
Imagine a shopping cart. The nav needs to show the item count. The sidebar lists the items. The checkout button needs to disable itself when the cart is empty. In jQuery, each of those components reads the DOM to get the "current" count -- or maintains its own copy in a hidden element or a global variable.
Now an item gets removed. You need to update the nav count, the sidebar list, and the checkout button state.
Three separate mutations to coordinate. Manually.
ExpandTwo panels: left shows four functions (updateNav, syncCart, checkout, removeItem) all pointing arrows into a single DOM element — no single owner. Right shows a React component owning state at the top with a single arrow down to a passive DOM output box.
Miss one and the UI lies. The nav says 3, the sidebar shows 2, the checkout button stays enabled with an empty cart. None of those are bugs in any single function.
The system itself is broken.
Why State in the DOM Is the Root Cause
The core problem is not jQuery. The problem is using the DOM as the source of truth.
When state lives in the DOM, multiple functions read from and write to the same HTML elements. There is no single owner. No one function is responsible for the count -- every function is, which means none of them actually are.
As the app grows, this creates what developers call spaghetti code: a web of event listeners that all touch the same DOM elements, in no predictable order, with no clear ownership structure.
Any function can change any element at any time.
Nothing enforces order. Nothing prevents conflicts.
What React Did Differently
React moved state out of the DOM and into JavaScript.
Your component owns a piece of state. It describes what the UI should look like given that state. React writes to the DOM. You never do.
function Cart() {
const [items, setItems] = useState<string[]>([]);
return (
<div>
<p>{items.length} items in cart</p>
<button onClick={() => setItems([])}>Clear cart</button>
</div>
);
}There is no getElementById anywhere. There is no reading a text node to figure out the current count. The state is items. The UI is a description of what to show when items has a particular value.
When items changes, React re-renders the component. The DOM reflects the new state.
You never had to coordinate the update manually. React did.
Imperative vs Declarative
This is the shift from imperative to declarative code.
Imperative: "Find the nav element. Read its text. Parse it as a number. Add one. Write the new number back."
Declarative: "The nav shows items.length. When items changes, show the new length."
Imperative code describes how to change the UI step by step. Declarative code describes what the UI should look like, and the framework figures out the steps.
React's job is to take your description and translate it into the minimum set of DOM mutations needed to make it true. The Virtual DOM and the Fiber reconciler -- both covered later in this series -- are the machinery that makes that translation fast.
Why This Actually Matters
When state is a JavaScript value owned by a component, there is one source of truth. Every part of the UI that needs the cart count reads from the same state. When that state updates, everything that depends on it re-renders. No manual coordination.
You update the data.
React updates the DOM.
This is not just a convenience. It is a different architecture for who is responsible for what. The component owns its state. The DOM is an output of that state. Nothing writes to the DOM directly.
That is the foundation everything else in React is built on. Components, hooks, the reconciler, server components -- all of it exists to make this model work reliably as the app grows.
The next post goes one layer deeper: the JSX syntax that lets you describe your UI in something that looks like HTML but is actually something quite different under the hood.
The Essentials
- State in the DOM is the root problem jQuery could not solve at scale. When multiple functions read and write to the same DOM elements, there is no single source of truth and coordinating updates becomes impossible.
- React moved state into JavaScript. Your component owns its state. The DOM is an output -- React writes to it, you never do directly.
- Declarative over imperative. You describe what the UI should look like given the current state. React figures out which DOM mutations are needed to get there.
Further Reading and Watching
- React in 100 Seconds -- Fireship: A fast visual overview of how React's component model maps to the DOM.
- Thinking in React -- React Docs: The official guide to the mental model behind React's component and state design.
Keep reading