Why does clicking price sort in a table put 10 before 2 in 2026? Is sort comparing strings by default?
Conclusion first: When no compare function is passed, Array.prototype.sort first converts elements to strings and then compares them by UTF-16 code units, so [2, 10, 1] commonly sorts to [1, 10, 2]. 10 coming before 2 is neither a browser bug nor a new pitfall that appeared only in 2026; it is a long-standing rule. To sort by price, stock, quantity, or date, you need to pass a compare function explicitly and write null-value placement, whether the array is modified in place, and whether pagination is involved into the acceptance criteria. Otherwise it may look right locally and then break with another batch of data.
What the default comparison does, and why 10 comes before 2
Default sorting has two steps: first convert each element to a string, then compare UTF-16 code units one by one. The number 10 becomes '10', and the number 2 becomes '2'. In a unit-by-unit comparison, the first character '1' is less than '2', so 10 is placed before 2. Numeric strings '10' and '2' produce the same result, so when a backend returns numeric fields as strings, the same type of problem keeps recurring.
This rule is not an ES6+ feature; it is comparison logic inherited from early JavaScript. Since ES2019, the specification requires stable sorting, but stability only guarantees that equal elements keep their relative order; it does not change the default comparison method. There is only one criterion for deciding whether to handle it: whether the sort field is numeric, an amount, a date, or a business-meaningful ID.
- Default sorting is acceptable for: lexicographic order of purely alphabetic strings, status codes displayed according to a convention.
- Default sorting should not be used for: price, stock, sales volume, age, timestamps.
- Easy to overlook: when the API returns null, undefined, or an empty string, the default behavior places them at one end, which may not be what the business wants.
Delivery scene: sorting only the current page makes order inconsistent after pagination
Constraints: a major promotion activity list where the API returns data page by page, 20 items per page. To avoid waiting for backend scheduling, the front end first sorts the current page locally by price ascending. The approach looks convenient, and the compare function is written correctly; local self-testing passes.
After launch, the problem appears on the second page: when users view the first two pages together, they find that overall prices are not ascending. Customer service forwards a screenshot, and the business side first assumes the data is wrong. The cost is about 0.5-1 person-day of rework, plus an extra test submission. The front end removes the compare function and changes the API to pass the sort field and direction back to the server. Later, the team adds a fixed line to the integration ticket: sort field type + whether server-side sorting is used + whether nulls go first or last. This kind of rework basically stopped appearing. Based on experience range, rework for this kind of local patch usually falls between half a day and two days, depending on whether it has already gone live.
The most valuable part of this experience is not how to write the compare function, but asking one question in advance: is this data the full set? In pagination, infinite scrolling, and multi-client shared scenarios, no matter how accurate local sorting is, it can only sort the segment in front of you.
Default sorting, explicit front-end comparison, and server-side sorting: trade-offs
The three approaches are not replacements for one another; they divide work according to data volume and consistency requirements. A common 2026 approach is: display lists that are fetched all at once and have a limited number of items are sorted on the front end; paginated lists, multi-client shared lists, and lists whose rules may change are left to the server. The following comparison is organized by cost and scope for checking; all values are experience ranges, and specifics depend on fields and indexes.
- Default sorting: development cost is close to zero, and it is only suitable for purely string lexicographic display; it is not suitable for amounts, quantities, dates, or business order that needs to be written into API documentation.
- Explicit front-end compare function: development plus self-testing commonly takes 0.5-2 hours, suitable for hundreds to thousands of records already fetched locally at once; the cost is extra handling for null normalization and copying the original array.
- Server-side sorting: API changes usually fall in the 0.5-2 person-day experience range, depending on fields and indexes, suitable for pagination, large data volumes, and multi-client consistency; the cost is dependence on backend scheduling and a longer integration chain.
The compare function itself has several bottom lines: for numbers use (a, b) => a - b, not a > b that returns a boolean; for dates, convert to timestamps before subtracting; for Chinese names that need stable ordering, use localeCompare with an explicit locale, and verify it in the target environment rather than only checking results on the development machine.
When front-end local sorting is suitable, and when it should not be touched
The criterion is not whether the front end can write it, but whether the sorted result must stay consistent across pages, clients, and roles.
- Suitable: data is returned all at once, with hundreds to thousands of records at most; sorting only affects the current view, is not written back to the API, and is not used as a basis for export or reports; rules are simple and stable, such as sorting by name or by a conventional display order.
- Not suitable: paginated or infinite-scroll lists where the front end sees only partial data; fields such as amounts, stock, or sales volume that directly affect user decisions; Web, mini-program, and App clients requiring the same order; sorting rules are still changing frequently.
- In between: the number of records is small but fields contain nulls. You can normalize first and then sort on the front end, provided that null placement is written into the acceptance checklist rather than left to default behavior.
Frequently asked questions
Why does [10, 2, 1].sort() return [1, 10, 2]?
Because default sorting first converts elements to strings and compares them unit by unit, and the first character '1' in '10' is less than '2', so 10 comes first; to sort numerically, pass a compare function.
Can a compare function return true or false?
No. A compare function must return a negative number, 0, or a positive number. Returning a boolean is treated as 0 or 1 in the comparison, and the result depends on engine implementation, so it is easy to break with another batch of data.
Does sort modify the original array?
Yes, sort sorts in place. If you need to keep the original array, copy it before sorting, commonly with the spread operator or slice; otherwise you may modify filter data shared by other components.
Is writing only a - b enough for numeric sorting?
For regular numbers, it is enough. If the data contains NaN, null, undefined, or empty strings, normalize first or write separate rules for nulls first or last; otherwise the result may not match business expectations.
The next time list order looks wrong, first confirm the sort field type and whether the data is paginated, then check whether a compare function is passed, and finally write null placement and multi-column priority into the acceptance checklist. Applicable boundary: front-end local sorting is only suitable for display lists that are fetched in full at once, have a limited number of records, and do not require cross-client consistency; paginated, amount-related, and multi-client shared scenarios should use server-side sorting, with cost assessed by experience range rather than falling back to default sorting.
-
In 2026, why does a React admin app run fine in the morning but lag after opening a few detail pages in the afternoon, then recover after a browser restart—are listeners and timers not being cleaned up?
Date: Nov 1, 2026 Read: 3
-
In 2026, clicking “Get verification code” in a modal submits the whole form too—is the button’s default type the culprit?
Date: Oct 30, 2026 Read: 17
-
In 2026, AI can ship a homepage fast, but images break and buttons stop responding on target devices—should frontend still own compatibility and performance?
Date: Oct 29, 2026 Read: 35
-
It's 2026—when my performance review asks what separates a frontend experience architect from a regular frontend developer, what evidence actually counts?
Date: Oct 28, 2026 Read: 26
-
It's 2026—why does a campaign page countdown fall behind after a few minutes on the lock screen, and why do users send screenshots asking if we cut their time?
Date: Oct 27, 2026 Read: 29




