Everything Vanilla JavaScript Has Gained Since React Launched
React was announced and open-sourced in May 2013. In the thirteen years since, the framework conversation has moved through Angular 2, Vue, Redux, Next.js, and a dozen others, each pitched, at some point, as solving problems the language itself couldn’t. Less discussed is what happened to the language itself in that same window. This is a dated, organized account of it: what JavaScript itself, the language, the browser platform, and the Node.js runtime, actually gained between 2013 and now.
Rundown
- ECMAScript has shipped a new edition every year since 2015, moving from ES6’s foundational rewrite (
let/const, classes, modules, Promises, arrow functions) through ES2025, finalized June 25, 2025, and ES2026, ratified June 30, 2026. - Real, substantial capability shipped in nearly every one of those editions:
async/await, optional chaining, nullish coalescing, private class fields, top-levelawait, immutable array methods, native Set operations, and lazy iterator helpers, among many others. - The browser platform gained native capabilities that used to require a library: the Fetch API, IntersectionObserver and ResizeObserver, native Web Components, and more.
- Node.js absorbed several of these directly into its runtime: built-in
fetch(stable since Node 18), a built-in test runner (stable since Node 20, with watch mode reaching stable a bit later), andnode:sqlite. - The single biggest thing still missing, framework-level reactivity, is real and in progress: TC39’s Signals proposal has the broadest framework-author collaboration in JavaScript’s history behind it, but it’s still Stage 1, not shipped, and not part of this history yet.
The ECMAScript timeline, year by year
Per TC39’s own specification history, each edition below reached Stage 4 and shipped on the annual June release cycle since 2015:
| Edition | Year | Selected additions |
|---|---|---|
| ES2015 (ES6) | 2015 | let/const, arrow functions, classes, template literals, destructuring, default/rest/spread parameters, Promises, generators, Map/Set, native modules (import/export) |
| ES2016 | 2016 | Array.prototype.includes, exponentiation operator (**) |
| ES2017 | 2017 | async/await, Object.entries/Object.values, String.padStart/padEnd |
| ES2018 | 2018 | Object rest/spread properties, async iteration (for await...of), Promise.prototype.finally, RegExp named capture groups |
| ES2019 | 2019 | Array.flat/flatMap, Object.fromEntries, String.trimStart/trimEnd, optional catch binding |
| ES2020 | 2020 | Optional chaining (?.), nullish coalescing (??), BigInt, dynamic import(), Promise.allSettled, globalThis |
| ES2021 | 2021 | String.replaceAll, Promise.any, logical assignment operators (||=, &&=, ??=), numeric separators |
| ES2022 | 2022 | Top-level await, public and private class fields, Object.hasOwn, Array/String/TypedArray.prototype.at, RegExp match indices |
| ES2023 | 2023 | Immutable array methods (toSorted, toReversed, toSpliced, with), Array.findLast/findLastIndex, Symbols as WeakMap keys |
| ES2024 | 2024 | Object.groupBy/Map.groupBy, Promise.withResolvers, resizable ArrayBuffers, String.isWellFormed/toWellFormed, RegExp /v flag |
| ES2025 | 2025 | Native Set operations (union, intersection, difference, and more), lazy Iterator Helpers, Promise.try, RegExp.escape, JSON modules |
| ES2026 | 2026 | Explicit resource management (using/await using), Math.sumPrecise, Error.isError, Uint8Array base64/hex methods, Array.fromAsync |
Two things need precision in that table. First, the Temporal API, JavaScript’s long-awaited replacement for the notoriously broken Date object, reached Stage 4 (meaning finished and guaranteed to ship) at TC39’s March 2026 meeting. Date has been a widely acknowledged design failure since JavaScript’s earliest days, mutable, timezone-unaware, and a common source of real production bugs, so Temporal being finished after roughly a decade of work is one of the more consequential single additions in this entire window. But per TC39’s own finished-proposals tracking, its expected publication year is 2027, not 2026, it crossed the finish line just after the cutoff for this year’s edition. Several 2026 roundups reported it as part of ES2026; the primary source says otherwise, and that’s the version worth trusting.
Second, this table covers language syntax and built-ins specifically, the part of JavaScript that runs everywhere, in a browser or on a server, standardized through TC39. The platform and runtime gained real capability too, covered next, through entirely separate standards processes, split the same way this account is: what the browser gained, then what Node.js absorbed directly.
What the browser platform gained
Several capabilities that used to require a library became native browser APIs in this window. The Fetch API, a Promise-based replacement for XMLHttpRequest, shipped across major browsers starting in 2015. IntersectionObserver and ResizeObserver gave the platform a native, performant way to react to an element’s visibility and size changes, replacing what used to mean hand-rolled scroll and resize listeners. Native ES modules landed in browsers via <script type="module">in 2017, letting import/export run directly without a bundler. Web Components, Custom Elements and Shadow DOM specifically, matured into a real, usable standard for building reusable, encapsulated HTML elements without a framework, the same category of problem component frameworks were originally built to solve.
What Node.js absorbed directly
Node’s own runtime picked up several of these as built-ins rather than requiring a dependency. fetch shipped as experimental in Node 17.5 (February 2022) and became stable and enabled by default in Node 18 (April 2022), the same release that introduced a built-in test runner. That test runner module became stable in Node 20 (April 2023), though watch mode specifically remained experimental a while longer, only reaching stable in the later Node 20.13.0 release. A built-in node:sqlite module followed in later releases. None of these individually replace a framework, but together they represent real, measurable narrowing of the gap between what Node does on its own and what used to require node-fetch, Jest, and a database driver just to get started.
The next big one isn’t here yet
The most consequential remaining gap between vanilla JavaScript and what frameworks provide is reactivity, and it hasn’t shipped. TC39’s Signals proposal aims to bring a native, standard reactive primitive to the language, backed by authors and maintainers from Angular, Vue, Solid, Preact, Svelte, Qwik, Ember, and MobX, described as the broadest framework-author collaboration in JavaScript’s history. Several of those frameworks have already converged on Signals as their own reactivity model ahead of any native standard. But as of this writing, the proposal is Stage 1, meaning the core API isn’t even finalized. It’s a real, serious effort with real momentum behind it, not a rounding error, but it belongs in a future edition of this account, not this one.
None of this adds up to vanilla JavaScript now doing everything a framework does. Component composition patterns, large-team state management conventions, and native reactivity once Signals actually ships are real gaps a bare script tag doesn’t close on its own. What’s changed, concretely and by version, is how much smaller that gap has gotten for the specific category of problem a lot of sites actually have.
Related reading
This connects directly to why server-rendered templating doesn’t need a build step, which makes a related argument about a narrower slice of this same territory, and sets up the more argumentative case for how much of this capability is actually worth using directly.