{"id":"skbajvgjb34ddio","title":"How React became the undisputed engine of the modern web","slug":"how-react-became-the-undisputed-engine-of-the-modern-web","summary":"React became the default because it matched how teams build modern interfaces. The challenge now is using it carefully enough that the app still feels fast and simple.","imageUrl":"https://briancrabtree.me/images/journal-how-react-became-the-undisputed-engine-of-the-modern-web.webp","category":"Web Development","date":"2025-12-23","featured":false,"likes":42,"author":"Brian Crabtree","content":"<h2>The Shift to Component Architecture</h2>\n\n<p>Back in the day, we used to separate technologies. We put HTML in one file, CSS in another, and JavaScript in a third. We called this \"separation of concerns,\" and we patted ourselves on the back for being organized. The problem was that this separation was purely cosmetic. If you changed a class name in your HTML, you broke your CSS and your JavaScript. The actual \"concern\" was the UI element itself—the button, the navbar, the form—and its logic was scattered across three different locations. It was a maintenance nightmare that got exponentially worse as the project grew.</p>\n\n<p>React forced us to stop lying to ourselves. It introduced a component-based architecture where the logic, the structure, and often the styling of a specific element live together. This might sound messy to a purist, but in practice, it is the only way to build large-scale applications without losing your mind. When we build a navigation bar in React, it is a self-contained entity. It knows how to render itself, it knows how to handle clicks, and it holds its own state. If I drop that component into a completely different page, it works. This modularity means we are not rewriting code constantly. It means that when a client asks to change the behavior of a signup form, we change it in one place, and that update propagates everywhere instantly. It transforms the development process from a fragile balancing act into an assembly of robust, interchangeable parts.</p>\n\n<pre><code>import { createRoot } from 'react-dom/client';\ncreateRoot(document.getElementById('root')).render(&lt;App /&gt;);</code></pre>\n\n<h2>Why the Virtual DOM Actually Matters</h2>\n\n<p>Browsers are notoriously slow when it comes to rendering. The DOM, which is the browser's internal representation of your web page, was never designed for the high-frequency updates required by modern interactive applications. Every time you use JavaScript to touch the DOM—to add a class, remove an element, or change text—the browser has to do a massive amount of calculation to figure out where everything goes and repaint the pixels. If you do this too often, your expensive MacBook starts to sound like a jet engine and the user interface becomes sluggish.</p>\n\n<p>React solves this with the Virtual DOM. It sounds like a buzzword, but the engineering behind it is solid. React keeps a lightweight copy of the interface in memory. When your data changes, React does not touch the browser immediately. Instead, it updates its memory copy and then runs a very fast algorithm to compare the new version with the old version. It figures out the absolute minimum number of changes required to make the real screen match the data. It then batches those updates and applies them in a single sweep. This \"diffing\" process means we can write code as if we are re-rendering the whole page every time, without paying the performance penalty. We stop worrying about manual optimization and let the library handle the dirty work of browser painting.</p>\n\n<h2>The Sanity of Declarative Code</h2>\n\n<p>Most older programming for the web was imperative. You had to micromanage the browser. You wrote code that said: \"Find the button with ID 'submit', then listen for a click, then find the div with ID 'error', then check if the input is empty, then add the class 'visible' to the error div.\" You were describing the step-by-step process of transition. This approach is brittle. If you miss a step, or if the user clicks things in an unexpected order, the UI gets out of sync with the data. You end up with loading spinners that never disappear or error messages that show up on success screens.</p>\n\n<p>React pushes a declarative paradigm. We do not tell the browser *how* to change. We simply tell it *what* the interface should look like given a specific state. We say: \"If the form is loading, show a spinner. If there is an error, show the error message.\" React handles the transition. This drastically reduces the surface area for bugs. The code becomes more predictable because the visual output is a pure function of the current data. You can look at the code and know exactly what the user will see, without having to mentally simulate a dozen sequential steps of DOM manipulation.</p>\n\n<h2>Predictable Data Flow</h2>\n\n<p>One of the biggest mistakes in early frameworks like AngularJS was two-way data binding. It seemed magical at first—you change the model, the view updates; you change the view, the model updates. But in a complex app, this created a chaotic web of events where a change in one place would trigger a cascade of updates that were impossible to trace. You would fix a bug in the footer and break the header.</p>\n\n<p>React enforces a strict unidirectional data flow. Data flows down the tree like a waterfall. A parent component passes data (props) down to its children. If a child component needs to change something, it does not modify the data directly; it sends a signal back up to the parent requesting a change. This seems restrictive at first, but constraints are what make software maintainable. Because data only moves one way, it is incredibly easy to trace bugs. If a component is rendering the wrong information, you know exactly where that information came from. It eliminates the \"spooky action at a distance\" that plagues so many legacy codebases.</p>\n\n<h2>The Practical Reality for Business</h2>\n\n<p>You might be wondering why any of this technical nuance matters to a business owner or a project manager. It matters because technical debt is a real financial liability. When we build with tools that encourage spaghetti code, the cost of adding new features doubles with every release. Stability drops. Development slows to a crawl as the team spends more time fighting the existing code than building new value.</p>\n\n<p>By using React, we are opting into an ecosystem that prioritizes stability and maintainability. It allows us to build interfaces that feel immediate and responsive, which is the baseline expectation for users today. More importantly, it allows us to hand over a product that does not require a complete rewrite two years down the line. It is about respecting the investment. We use these tools not to show off, but to ensure that the complex, dynamic applications we build remain performant and manageable long after the initial launch. For a related angle I keep coming back to, see <a href=\"/journal/static-prerender-shells-spa/\">Static Prerender Shells: SPAs That Paint Before JavaScript</a>.</p>","tags":["React","Web Design","JavaScript","Web Development","UI/UX Design","Front-end Development","ReactJS","UI Components","Modern Web","Web Applications"],"views":113}