The ES2023 specification now ships four array methods—toSorted, toReversed, toSpliced and with—that return new arrays instead of mutating the original. In React and other UI libraries that rely on immutable state, these helpers let developers replace the spread-operator tricks that have long been a source of bugs and boilerplate.

Why the change matters

React decides whether to re-render a component by comparing the previous state reference with the new one. If the reference is unchanged, React assumes nothing has changed. The classic Array.prototype.sort method sorts the array in place and returns the same reference, so a call like setTasks(prev => prev.sort(fn)) leaves React blind to the update. The UI stays stuck on stale data, a bug that shows up all too often in real projects.

Developers have worked around the problem by cloning the array first—typically with the spread operator—so that the sorting step produces a fresh reference:

setTasks(prev => [...prev].sort((a, b) => b.priority - a.priority));

That pattern works, but it adds noise and is easy to forget. The new ES2023 methods give a direct, readable way to produce a new array while keeping the original untouched.

The four non-mutating methods

  • toSorted(compareFn?) – behaves like sort but returns a sorted copy. No change to the source array.
  • toReversed() – replaces reverse. It yields a reversed copy, leaving the original order intact.
  • toSpliced(start, deleteCount, ...items) – mirrors splice without side effects. The returned array reflects the insertion or removal, the source stays the same.
  • with(index, value) – substitutes the element at index with value and returns a new array. It replaces the common pattern of map or spread-operator element replacement.

All four methods are part of the ECMAScript standard and are available in current versions of major browsers and Node.js 20.

How code looks now

Sorting a list

// Before
setTasks(prev => [...prev].sort((a, b) => b.priority - a.priority));

// After
setTasks(prev => prev.toSorted((a, b) => b.priority - a.priority));

Updating a single item

// Before
setItems(prev =>
  prev.map((item, i) => (i === idx ? newItem : item))
);

// After
setItems(prev => prev.with(idx, newItem));

Reversing an array

setLogs(prev => prev.toReversed());

Removing an element

setTags(prev => prev.toSpliced(removeIdx, 1));

The new syntax removes the need for extra spread operators or mapping loops, making state updates easier to read and less error-prone.

Who benefits and who might hesitate

Developers using React, Vue, Redux, Zustand, or any framework that expects immutable data structures gain a clearer mental model: call a method, get a new array, hand it back to the setter. The reduction in boilerplate can also shave a few milliseconds off render cycles, because the engine avoids creating an intermediate copy before sorting.

Teams with legacy browsers may need to include polyfills. The methods are not present in older versions of Safari or Internet Explorer, so a production build that targets those platforms must bundle a fallback. That adds a small bundle-size penalty, but the trade-off is often worth the readability gain.

Library authors might need to update type definitions (e.g., TypeScript) to expose the new signatures. Until those definitions land in the official @types packages, developers may see temporary type errors.

What to watch next

  • Adoption metrics – tooling such as ESLint may soon add rules that flag mutable array calls in state setters, nudging developers toward the new methods.
  • Performance studies – early benchmarks suggest the native non-mutating methods are faster than a spread-operator clone followed by a mutable operation, but real-world data will confirm the impact.
  • Further proposals – the ECMAScript committee continues to explore immutable-by-default APIs; keeping an eye on upcoming stages could reveal more helpers that fit the same pattern.

Takeaway

The latest ECMAScript specification gives UI developers a built-in, concise way to keep state immutable without the spread-operator gymnastics that have caused countless bugs. By swapping sort, reverse, splice and index-based replacements for toSorted, toReversed, toSpliced and with, you let React’s change detection work as intended and make your code easier to read. If your target browsers support the new methods—or you’re willing to polyfill them—it’s time to retire the old patterns and let the language do the heavy lifting.