How to sort an array of objects by property in JavaScript?
Sort an array of objects by a string, number or date property with a compare function, sort by several keys, and avoid the default sort and mutation traps.
Pass a compare function to sort(). For strings, use localeCompare(); for numbers, subtract one value from the other.
const users = [
{ name: 'Zoe', age: 31 },
{ name: 'adam', age: 25 },
{ name: 'Émile', age: 40 },
];
users.sort((a, b) => a.name.localeCompare(b.name));
// adam, Émile, Zoe
users.sort((a, b) => a.age - b.age);
// adam (25), Zoe (31), Émile (40)The compare function gets two items and returns a number. Negative puts a first, positive puts b first, and 0 keeps their current order. sort() is stable, so items that compare equal stay in the order they were in.
localeCompare() sorts the way people expect. A plain < comparison compares character codes, so it would put Zoe before adam (uppercase letters come first) and Émile after both.
Descending order
Swap a and b:
users.sort((a, b) => b.age - a.age);
users.sort((a, b) => b.name.localeCompare(a.name));Sorting by several properties
Chain the comparisons with ||. When the first one returns 0 (a tie), 0 is falsy, so the next comparison decides:
// by role A to Z, then oldest first within each role
users.sort(
(a, b) =>
a.role.localeCompare(b.role) ||
b.age - a.age
);Dates
Date objects can be subtracted, because they turn into timestamps:
posts.sort((a, b) => b.date - a.date); // newest firstISO date strings like '2026-09-16T12:00:00Z' sort correctly as plain strings, as long as they all use the same format and time zone, so a.date.localeCompare(b.date) works. For other formats, convert first: new Date(b.date) - new Date(a.date).
sort() changes the original array
sort() sorts in place and returns the same array, so const sorted = users.sort(...) isn't a copy. That matters with React state or props, and whenever other code uses the array. Use toSorted() (ES2023) or copy first:
const byAge = users.toSorted((a, b) => a.age - b.age);
// the same, for older environments
const byAgeCopy = [...users].sort((a, b) => a.age - b.age);The default sort compares strings
Without a compare function, sort() turns every item into a string and compares character codes. That's why numbers come out in a strange order, and why objects don't get sorted at all (each one becomes "[object Object]"):
[10, 9, 1].sort(); // [1, 10, 9]
[10, 9, 1].sort((a, b) => a - b); // [1, 9, 10]Don't return a boolean
// wrong
[3, 1, 2].sort((a, b) => a > b); // [3, 1, 2] in Chrome and Nodetrue becomes 1 and false becomes 0, so the function never returns a negative number. The engine treats many pairs as equal, and the result depends on the browser's sorting algorithm. It can even look right in a quick test. Always return a number.
Natural and case-insensitive order
localeCompare() puts item 10 before item 2, because it compares character by character. For natural order, use Intl.Collator with numeric: true. sensitivity: 'base' also makes a, A and á compare as equal:
const collator = new Intl.Collator(undefined, {
numeric: true,
sensitivity: 'base',
});
const files = [
{ name: 'item 10' },
{ name: 'Item 2' },
{ name: 'item 1' },
];
files.sort((a, b) => collator.compare(a.name, b.name));
// item 1, Item 2, item 10Creating one collator and reusing it is faster than passing the same options to localeCompare() for every comparison, which matters for large arrays.
If the property can be missing, a.name.localeCompare() throws a TypeError. Fall back to an empty string: (a.name ?? '').localeCompare(b.name ?? '').