Array.prototype.toSorted()
One of four "change by copy" methods added in ES2023. It fixes sort's mutation problem and nothing else — the string-comparison trap comes along unchanged.
Demo
Compare this with sort and you will see identical output — [10, 9, 1] still becomes [1, 10, 9], because toSorted inherits the default string comparator wholesale. The ONLY thing it changes is that the source array is untouched, which the demo cannot show. If you were hoping the newer method also fixed the number sorting, it does not: you still need (a, b) => a - b.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| compareFn | Function | no (string comparison) | Same contract as sort: return negative, zero or positive. Omitted, elements are compared as strings — exactly as sort does. |
Return value
Array — A NEW sorted array. The original is left exactly as it was — the only difference from sort.
Common patterns
setItems(items.toSorted((a, b) => a - b));
const shown = props.rows.toSorted(byName);
const sorted = [...items].sort(cmp);
Examples
Pitfalls
[10, 9, 1].toSorted()
[10, 9, 1].toSorted((a, b) => a - b)
const a = [{n: 1}]; const b = a.toSorted(); b[0].n = 9; a[0].n
const b = structuredClone(a).toSorted(cmp);
items.toSorted(cmp)
[...items].sort(cmp)
When to use
- Sorting state, props or any array you must not mutate
- Chaining a sort into an expression without a separate copy step
- Anywhere [...items].sort(...) appears today
- You own the array and want to sort in place → sort is cheaper
- Runtimes older than ES2023 → [...items].sort()
- You need the nested objects independent too → deep copy first
Notes
FAQ
No — it has exactly the same default as sort, comparing elements as strings. The ES2023 methods addressed mutation, not comparison. Pass (a, b) => a - b for numbers, just as you always did.
[10, 9, 1].toSorted() // [1, 10, 9] [10, 9, 1].toSorted((a, b) => a - b) // [1, 9, 10]