Every New JavaScript Feature Worth Knowing Right Now
JavaScript has been adding features at a pace we have not seen in years. Between ES2025, ES2026, and a few ES2027 proposals that just crossed the finish line recently, there are a lot of genuinely useful things landing in browsers right now. Some of these fill gaps that have been around for years, like doing set math without converting everything to arrays first or handling binary data without reaching for a library. Others, like using for resource cleanup and the Temporal date API, are the kinds of features that will reshape how you think about whole categories of problems and are tools I have been waiting years to use.
I am going to start with the features available in all browsers and end with the latest features that I am most excited about that are still in the process of rolling out.
New Set Methods (ES2025)
If you have ever needed the common elements between two arrays, you have probably written something like arr1.filter(x => arr2.includes(x)). It works, but it is not great. It also does not generalize to any of the other set operations you might need.
Set now has all seven set theory methods built in:
const a = new Set([1, 2, 3, 4])<br>const b = new Set([3, 4, 5, 6])
a.union(b) // Set {1, 2, 3, 4, 5, 6}<br>a.intersection(b) // Set {3, 4}<br>a.difference(b) // Set {1, 2}<br>a.symmetricDifference(b) // Set {1, 2, 5, 6}
a.isSubsetOf(b) // false<br>a.isSupersetOf(b) // false<br>a.isDisjointFrom(b) // false<br>None of these mutate the original set. union, intersection, difference, and symmetricDifference all return a new Set. The last three return a boolean.
The thing I find most useful is that these methods accept any iterable as their argument, not just another Set. So you can pass an array directly:
const evens = new Set([2, 4, 6, 8])<br>evens.intersection([3, 4, 5, 6]) // Set {4, 6}<br>These are now baseline and available in all modern browsers. If you want to brush up on how Set itself works, check out my JavaScript Sets article.
Iterator Helpers (ES2025)
This is a feature that is underrated, but super exciting. Arrays have had helper methods like .map(), .filter(), and .reduce() forever, which makes chaining transformations easy. Iterators and generators have never had this, which meant working with them was far more awkward than it needed to be.
Iterator helpers add a full suite of lazy, chainable methods directly to the iterator prototype:
function* numbers() {<br>let i = 0<br>while (true) yield i++
const result = numbers()<br>.filter(n => n % 2 === 0)<br>.map(n => n * n)<br>.take(5)<br>.toArray()
// [0, 4, 16, 36, 64]<br>The key word there is lazy . Unlike array methods, these do not process every element upfront. The take(5) call stops the entire chain after 5 values, which makes this completely safe to use with infinite generators like the one above. With array methods you could never do this since .filter() would try to process an infinite number of elements.
The full set of available methods includes .map(), .filter(), .take(), .drop(), .flatMap(), .reduce(), .forEach(), .some(), .every(), .find(), and .toArray(). They work the same way as their array equivalents.
You can also use these on a regular array by calling .values() first:
const result = [1, 2, 3, 4, 5, 6, 7, 8]<br>.values()<br>.filter(n => n % 2 === 0)<br>.map(n => n * n)<br>.toArray()
// [4, 16, 36, 64]<br>These are now baseline and available in all modern browsers. For a deeper look at generators and iterators, see my generators article.
If you are unfamiliar with the base array methods in JavaScript you should<br>checkout my free Array Methods Cheat<br>Sheet.
Promise.try (ES2025)
This one is small but solves a real annoyance. If you have a function that might throw an error or return a rejected promise, you would normally need to wrap it in a try/catch block since .catch() only handles rejected promises, not synchronous throws:
// This does NOT catch synchronous throws<br>somePromise.then(() => functionThatMightThrowSync())<br>Promise.try fixes this by converting any synchronous errors into rejections, so you can handle both cases with a single .catch():
Promise.try(() => {<br>if (!userId) throw new Error("No user ID") // sync throw<br>return fetch(`/api/users/${userId}`).then(r => r.json()) // async rejection<br>})<br>.then(user => console.log(user))<br>.catch(err => console.error(err)) // handles both cases<br>Any function you pass to Promise.try will have its sync throws converted to rejections, so your .catch() always fires regardless of where the error came from. This is now baseline in all modern browsers.
Import Attributes (ES2025)
You may not know this, but importing JSON files in JavaScript was not supported by JavaScript and required a bundler like Vite to work. This changes with Import Attributes, which let you import non-JS resources directly:
import config from "./config.json" with { type: "json" }
console.log(config.version)<br>The with { type: "json" } part tells the browser what kind of module this is. This is not just...