The Case for Vanilla JavaScript in 2026
React solved real problems in 2013. The browser had no component model, no module system, no encapsulation, and no declarative way to keep a view in sync with state. Building a non-trivial interface meant hand-wiring DOM updates and hoping the wiring held. React’s answer was good enough that it became the default, and the default outlived the conditions that produced it.
Every one of those gaps has since been filled by the platform itself.
Rundown
- The specific problems React was built to solve in 2013, components, modules, encapsulation, declarative updates, now have native answers: Custom Elements, ES modules, Shadow DOM, and template literals.
- The last eighteen months closed the remaining gaps quickly: the Popover API reached Baseline in January 2025, View Transitions in October 2025, and the Navigation API in January 2026.
- Shopify rebuilt its Polaris design system on Web Components, shipped October 1, 2025, and now recommends it over the React version for all new development.
- Removing the build step removes a category of daily friction: no transpile wait, no source maps between the code you wrote and the code that runs, and no exposure to the churn from webpack through Vite to Turbopack.
- Hydration mismatches are a bug class that exists only because a framework renders twice and has to reconcile the results.
- Dependency count is now a security liability in its own right. CISA issued an alert over the September 2025 npm compromise, and the self-propagating Shai-Hulud worm has returned in waves through 2026.
- The honest counterpoint: Stack Overflow’s 2025 developer survey placed React at 46.9% adoption among professional developers, far ahead of any vanilla-first approach in actual practice.
What the platform absorbed
The case for vanilla JavaScript rests on a specific claim: the things that made a framework necessary are now part of the platform. That claim is checkable.
| What React provided in 2013 | The native answer | Available since |
|---|---|---|
| Component model | Custom Elements v1 | 2016 to 2018, cross-browser |
| Style and DOM encapsulation | Shadow DOM v1 | 2016 to 2018, cross-browser |
| Module system (via bundler) | Native ES modules | 2017 |
| Bare specifier imports (via bundler) | Import maps | 2023, all major browsers |
| Declarative templating | Template literals, <template> | 2015 |
| Data fetching abstraction | Fetch API | 2015 |
| Server-rendered components | Declarative Shadow DOM | Baseline August 2024 |
| Overlay and dropdown primitives | Popover API | Baseline January 2025 |
| Animated view transitions | View Transitions API | Baseline October 2025 |
| Client-side routing primitives | Navigation API | Baseline January 2026 |
The right-hand column answers why this argument lands now rather than in 2020. Three of those primitives reached Baseline within about twelve months of each other, and the Navigation API, the one that most directly overlaps with what a client-side router exists to do, arrived in January.
Shopify made a real bet
Shopify rebuilt Polaris, its own design system used across the entire Shopify admin, on Web Components rather than React. The change shipped October 1, 2025. Shopify’s own GitHub repository for the old React-based Polaris confirms it directly: the company now recommends its new Web Components version for “new development,” with the React package kept only for existing apps. Shopify’s official developer documentation confirms the components work in any framework, loaded via a single script tag, with no React dependency required.
Polaris is the design system behind Shopify’s own admin interface and the UI foundation thousands of third-party apps build on. Moving that specific piece of infrastructure onto Web Components is a decision from a company with production scale and every incentive to pick whatever actually works best.
The New Stack documented the same broader pattern at the end of 2025: native browser APIs maturing into production-grade replacements for what frameworks once provided, and frameworks becoming optional layers rather than defaults.
Developer experience: shorter distance to the running code
The clearest daily difference is what sits between writing a line and seeing it run.
Server-rendered templating removes the build step entirely, and with it the transpile, bundle, and hydration stages that framework rendering depends on. Save a file, reload, done. There is no dev server to warm, no incremental compile to wait on, and no state where the browser is showing output from a build that finished thirty seconds ago.
It also changes debugging. With a build step, the code running in the browser is not the code in the editor, and a source map is the bridge between them. When that bridge is accurate, the cost is small. When it isn’t, a stack trace points at a line that doesn’t exist in the file it names. Without a build step the question doesn’t arise, since the file the browser loads is the file on disk.
The third cost is the toolchain itself. The standard build pipeline has moved through webpack, Browserify, Rollup, Parcel, esbuild, Vite, and now Turbopack, each migration bringing new configuration, new plugin ecosystems, and new failure modes. That churn is invisible in a feature comparison and unavoidable in a maintenance schedule. A project with no build step is not exposed to it.
What replaces a framework is smaller than expected. For interactivity, Alpine.js covers what content-driven sites actually use React for in roughly 15KB, attached directly to server-rendered markup rather than replacing it. A maintained vanilla to-do app case study, running since 2020, ships a fully animated 60fps clone of a real commercial product in 55KB unminified with no build step. Its own conclusion is unsentimental: it “validates the value of build steps and frameworks” for specific problems, while proving standard web technologies work well for the rest.
Reliability: fewer moving parts, fewer failure modes
Hydration is the clearest example of a bug class created entirely by the architecture. A framework renders markup on the server, ships it, then renders again on the client and reconciles the two. When the results differ, because of a timestamp, a locale, a random value, or a browser extension modifying the DOM before the framework reaches it, the mismatch surfaces as a warning, a visual flash, or a component that silently stops responding. Server-rendered templates with progressive enhancement never render twice, so there is nothing to reconcile and no mismatch to debug.
The more consequential reliability cost is what abstractions hide. Framework-managed caching creates a real observability gap: when the framework owns the cache, its hit rate, eviction behavior, and failure modes are hard to inspect and harder to tune. Rendering has the same property, since React’s server-side rendering blocks Node’s event loopby design, coupling render cost to request latency in a way that isn’t visible from application code.
Those are not theoretical. A production migration off Next.js recorded a 58% average CPU reduction and event loop P95 latency falling from 385ms to 1.45ms on identical infrastructure, alongside unplanned process restarts dropping from 270 to 2. Part of that came from doing less work per request. Part came from problems becoming visible that had been absorbed.
Framework behavior can also break things outside its own scope. Client-side navigation silently breaking social embeds is a failure mode that exists only because a routing abstraction sits between the URL and the document. And version churn carries its own risk: an undocumented memory leak tied to dynamic routes in a self-hosted Next.js deployment took real investigation to diagnose, the kind of problem that arrives in a minor version bump and belongs to whoever deployed it.
Security: the dependency tree is the attack surface
2025 turned dependency count from a theoretical concern into a recurring emergency. In September, a phishing attack against a single maintainer compromised 18 packages including chalk and debug, together accounting for roughly 2.6 billion weekly downloads. A week later came Shai-Hulud, the first self-propagating worm in npm’s history, which harvested credentials and republished itself through any package its victims maintained, reaching 500+ packages. CISA issued a formal alert. The campaign has returned in waves since, with one 2026 wave publishing 639 malicious package versions in about an hour.
The mechanism matters more than any single incident. A postinstall script runs with your user’s privileges on every install, in CI as well as on a laptop, and it runs for transitive dependencies nobody on the team chose. The count is rarely what developers expect: npm install --dry-run <package> reports how many packages a single install actually pulls, and running it against a framework and against a minimal server library is the fastest way to see the difference on your own machine.
None of this is an argument against open source, and a vanilla project still pulls packages. It is an argument about how many, and about how much of the tree was chosen deliberately. A direct comparison of Next.js and Express CVE historiesmakes the size difference concrete.
Platform code doesn’t expire
The web platform’s defining commitment is that it doesn’t break what already works. A page written against the DOM in 2015 still runs. fetch, ES modules, and Custom Elements will still work in a decade, because removing them would break too much of the web to be worth it.
Frameworks make no such promise, and reasonably so, since progress sometimes requires breaking changes. But the cost lands on whoever wrote the code. Angular 1 to Angular 2 was a rewrite rather than an upgrade. React class components to hooks changed how nearly every component was written. Next.js’s move from the pages router to the app router restructured the mental model of routing, data fetching, and rendering at once.
Each of those was defensible on its own terms. Collectively they describe a category of maintenance work that platform code doesn’t generate. Time spent learning fetch keeps paying out; time spent learning a specific data-fetching library’s v3 API stops paying out at v4.
The one gap still open
Native reactivity is the remaining piece. TC39’s Signals proposal is Stage 1, meaning the core API isn’t finalized, but it has the broadest framework-author collaboration ever assembled behind it, authors and maintainers from Angular, Vue, Solid, Preact, Svelte, Qwik, Ember, and MobX. Several have publicly stated intent to align their own reactivity systems with the standard once it ships rather than maintain separate implementations. Tooling is forming around it already: Lit’s labs team has published @lit-labs/signals, integrating the in-progress proposal and its polyfill directly into Lit’s component lifecycle. React hasn’t aligned with it, building its own compiler-based approach instead.
Until Signals ships, applications with heavy fine-grained client state are the clearest case where a framework still does something the platform doesn’t.
Where frameworks still win
The numbers say plainly that frameworks still dominate real-world practice. Stack Overflow’s 2025 developer surveyplaced React at 46.9% adoption among professional developers, well ahead of Angular (19.8%), Vue (18.4%), and Svelte (6.9%), with vanilla-first approaches not registering as a comparable category. That gap reflects something real: React’s ecosystem depth, hiring pool, and maturity make it the safer choice for long-lived products, particularly ones with distributed teams that benefit from conventions a framework enforces from day one.
TypeScript integration remains a genuine differentiator, Angular’s TypeScript-first design and Vue’s Composition API both offer enforced type safety across state management and component contracts that vanilla JavaScript doesn’t provide on its own. Team size is the other axis. A framework’s conventions cost more than they return when one or two people hold the whole design in their heads, and start returning more than they cost once enough contributors are working in parallel that shared structure prevents divergence. Where exactly that line falls depends on the project, but it exists, and a small team sits on the side of it where the overhead is real.
The strongest version of the argument is narrower than a claim that frameworks are finished. It’s that the default has inverted. Reaching for a framework used to be the safe choice that required no justification, and building without one was the decision that needed defending. On a content-driven site or a small team in 2026, that’s backwards.
Related reading
This builds on everything vanilla JavaScript has gained since React launched, what the browser platform gained, and what Node.js gained, along with what React itself has given back to the language.