I ship both, depending on the project. Every React project I've built has been the right call, and so has every plain-JavaScript one — because I picked based on what the page actually needed to do, not on habit. Here's the actual dividing line I use.
What Vanilla JS Actually Handles Fine
A marketing site, a landing page, a mostly-static content site with a few interactive touches — a dropdown, a form, a scroll animation — doesn't need a component framework, a build step, or a virtual DOM to feel good. Plain JavaScript (or a light sprinkle of it over server-rendered HTML) loads faster, has fewer moving parts to break, and is easier for the next developer to open and understand without first learning your framework's conventions.
Where React Starts Earning Its Keep
React's real value shows up once a page has to keep multiple pieces of UI in sync with changing data — a dashboard where updating one filter changes a chart, a table, and a summary all at once; a form wizard with conditional steps; anything with real-time updates. Trying to hand-manage that kind of state with plain DOM manipulation gets messy fast, and that mess is exactly the problem React was built to solve.
The tell I actually look for: if I catch myself writing code to manually keep three different parts of the page updated whenever one piece of data changes, that's the moment a framework stops being overhead and starts being the thing that keeps the code sane.
State Management Is the Real Dividing Line
Not page complexity, not line count — state. A page can have a lot of HTML and still be simple if nothing on it changes dynamically in response to anything else. A page can look simple and still be a strong case for React if a handful of values need to stay in sync across several UI elements in real time. I look at how much state actually needs to be tracked and re-rendered, not at how the page looks.
Team Size and Long-Term Maintenance
A framework also pays off differently depending on who's maintaining the code. A solo project or a small static site rarely needs the structure React imposes. A product with multiple developers touching the same codebase over months or years benefits from the conventions and component boundaries a framework enforces — it makes it easier for someone else to work in the code without accidentally breaking something two screens away.
A Framework Is a Trade, Not an Upgrade
React isn't strictly "better" than vanilla JavaScript — it trades simplicity and a smaller bundle for structure and easier state management at scale. That trade is worth making exactly when the state-management problem is real. Reaching for it by default, on a page that doesn't have that problem, adds a build pipeline, a bundle size, and a learning curve in exchange for nothing.
My Rule of Thumb
If the interface has real, interdependent state that needs to stay in sync across multiple elements, I reach for React. If it doesn't, plain JavaScript ships faster, loads faster, and is one less thing to maintain. I make that call project by project, not framework by default.