How to control whether uppercase or lowercase comes first in sorting
Use the caseFirst option in Intl.Collator to determine case ordering when sorting strings
Introduction
When you sort an array of strings that differ only in their capitalization, JavaScript needs to decide which version comes first. Should apple come before Apple, or should Apple come before apple? Different applications have different requirements for this ordering.
By default, the sort order for strings that differ only in case depends on the locale and sensitivity settings. In many cases, strings like apple and Apple are treated as equal, making their order unpredictable. When case ordering matters for your application, you need explicit control over whether uppercase or lowercase letters sort first.
The Intl.Collator API provides a caseFirst option that determines case ordering. You can configure the collator to place uppercase letters before lowercase letters, lowercase letters before uppercase letters, or use the locale's default behavior. This lesson explains how the caseFirst option works, when it has an effect, and how to use it in practical sorting scenarios.
What the caseFirst option does
The caseFirst option controls the order of strings that differ only in their capitalization. It accepts three string values: "upper", "lower", or "false".
When set to "upper", uppercase letters sort before their lowercase equivalents. The string Apple comes before apple.
When set to "lower", lowercase letters sort before their uppercase equivalents. The string apple comes before Apple.
When set to "false" (the string value, not the boolean), the collator uses the locale's default case ordering. This is the default behavior when you do not specify the option.
The caseFirst option only affects strings that are identical except for case. Strings that differ in their base letters sort according to normal alphabetical rules regardless of this setting.
How default case ordering works
Without specifying the caseFirst option, the collator uses default behavior that depends on the locale and sensitivity settings.
const collator = new Intl.Collator('en-US');
const words = ['apple', 'Apple', 'APPLE'];
words.sort(collator.compare);
console.log(words);
// Output: ['apple', 'Apple', 'APPLE']
The output order depends on the implementation and locale. With default sensitivity settings, the collator considers case differences, but the specific ordering of apple, Apple, and APPLE varies.
Different locales have different default case ordering conventions. Some locales traditionally place lowercase letters first, while others place uppercase letters first. Relying on default behavior produces inconsistent results across different environments.
Placing uppercase letters first with caseFirst upper
Setting caseFirst: "upper" ensures uppercase letters always come before their lowercase equivalents in sorted results.
const collator = new Intl.Collator('en-US', { caseFirst: 'upper' });
const words = ['apple', 'Apple', 'banana', 'Banana'];
words.sort(collator.compare);
console.log(words);
// Output: ['Apple', 'apple', 'Banana', 'banana']
The collator sorts alphabetically by base letter first, then applies case ordering within each group. Among words starting with a, Apple comes before apple. Among words starting with b, Banana comes before banana.
This ordering is useful when displaying lists where proper nouns or capitalized titles should appear prominently before common nouns.
const collator = new Intl.Collator('en-US', { caseFirst: 'upper' });
const names = ['smith', 'Smith', 'jones', 'Jones'];
names.sort(collator.compare);
console.log(names);
// Output: ['Jones', 'jones', 'Smith', 'smith']
Capitalized names appear before lowercase versions, making it easier to distinguish proper names from common words in mixed lists.
Placing lowercase letters first with caseFirst lower
Setting caseFirst: "lower" ensures lowercase letters always come before their uppercase equivalents in sorted results.
const collator = new Intl.Collator('en-US', { caseFirst: 'lower' });
const words = ['apple', 'Apple', 'banana', 'Banana'];
words.sort(collator.compare);
console.log(words);
// Output: ['apple', 'Apple', 'banana', 'Banana']
The collator sorts alphabetically first, then places lowercase versions before uppercase versions within each alphabetical group.
This ordering matches conventions where lowercase text is considered more standard or neutral, with uppercase reserved for emphasis or special cases.
const collator = new Intl.Collator('en-US', { caseFirst: 'lower' });
const tags = ['javascript', 'JavaScript', 'python', 'Python'];
tags.sort(collator.compare);
console.log(tags);
// Output: ['javascript', 'JavaScript', 'python', 'Python']
Lowercase tags appear first, which is useful when lowercase represents the canonical form and uppercase versions are less common variations.
Using caseFirst false for locale default behavior
Setting caseFirst: "false" (the string value) explicitly requests the locale's default case ordering. This produces the same result as omitting the option entirely.
const collator = new Intl.Collator('en-US', { caseFirst: 'false' });
const words = ['apple', 'Apple', 'APPLE'];
words.sort(collator.compare);
console.log(words);
// Output depends on locale implementation
The output order depends on what the locale considers standard. Some locales define uppercase first, others define lowercase first, and some treat case-differing strings as equal.
This option is useful when you want to make explicit that you are using locale defaults rather than accidentally omitting a configuration choice.
When caseFirst has no effect
The caseFirst option only affects comparison when the sensitivity setting allows case differences to matter. With sensitivity: "base" or sensitivity: "accent", case differences are ignored entirely, making the caseFirst option irrelevant.
const collator = new Intl.Collator('en-US', {
sensitivity: 'base',
caseFirst: 'upper'
});
const words = ['apple', 'Apple', 'APPLE'];
words.sort(collator.compare);
console.log(words);
// Output: ['apple', 'Apple', 'APPLE'] (or any order)
With base sensitivity, the collator treats all three strings as equal. The caseFirst option has no effect because case is not considered at all. The sort algorithm preserves the original order of equal items (stable sort), so the output matches the input order.
For caseFirst to have an effect, use sensitivity: "case" or sensitivity: "variant" (the default).
const collator = new Intl.Collator('en-US', {
sensitivity: 'variant',
caseFirst: 'upper'
});
const words = ['apple', 'Apple', 'APPLE'];
words.sort(collator.compare);
console.log(words);
// Output: ['APPLE', 'Apple', 'apple']
With variant sensitivity (which considers all differences including case), the caseFirst option determines the order of these case-varying strings.
Sorting names with consistent case ordering
When sorting user names or other proper nouns, consistent case ordering ensures predictable results across your application.
const collator = new Intl.Collator('en-US', { caseFirst: 'upper' });
const users = [
{ name: 'alice' },
{ name: 'Alice' },
{ name: 'bob' },
{ name: 'Bob' }
];
users.sort((a, b) => collator.compare(a.name, b.name));
users.forEach(user => {
console.log(user.name);
});
// Output:
// Alice
// alice
// Bob
// bob
The collator places capitalized names before lowercase versions. This ordering makes it easier to see proper names versus common words in mixed datasets.
Sorting file names with controlled case order
File systems have different case sensitivity behaviors. Some treat uppercase and lowercase as the same, others treat them as different. Explicit case ordering ensures your sorted file lists match user expectations regardless of the underlying file system.
const collator = new Intl.Collator('en-US', { caseFirst: 'lower' });
const files = ['README.md', 'readme.md', 'index.js', 'Index.js'];
files.sort(collator.compare);
console.log(files);
// Output: ['index.js', 'Index.js', 'readme.md', 'README.md']
Lowercase versions appear first, matching the convention where lowercase file names are more standard on Unix-like systems.
Combining caseFirst with numeric collation
The caseFirst option works alongside other collation options like numeric. You can sort strings with embedded numbers while controlling case ordering.
const collator = new Intl.Collator('en-US', {
numeric: true,
caseFirst: 'upper'
});
const items = ['item1', 'Item1', 'item2', 'Item2', 'item10', 'Item10'];
items.sort(collator.compare);
console.log(items);
// Output: ['Item1', 'item1', 'Item2', 'item2', 'Item10', 'item10']
The collator applies numeric comparison for the number portions and case ordering for the letter portions. Items sort by numeric value first (1, 2, 10), then by case within each numeric group.
Using the Unicode extension key for caseFirst
The caseFirst option can also be set through a Unicode extension in the locale identifier using the kf key. The extension values are upper, lower, and false.
const collator = new Intl.Collator('en-US-u-kf-upper');
const words = ['apple', 'Apple', 'banana', 'Banana'];
words.sort(collator.compare);
console.log(words);
// Output: ['Apple', 'apple', 'Banana', 'banana']
The locale string en-US-u-kf-upper specifies US English with uppercase-first case ordering. This produces the same result as passing { caseFirst: 'upper' } in the options object.
When both the Unicode extension and the options object specify caseFirst, the options object takes precedence.
const collator = new Intl.Collator('en-US-u-kf-upper', {
caseFirst: 'lower'
});
const words = ['apple', 'Apple'];
words.sort(collator.compare);
console.log(words);
// Output: ['apple', 'Apple']
The options object specifies caseFirst: 'lower', which overrides the kf-upper extension in the locale string. The collator uses lowercase-first ordering.
Checking what caseFirst value a collator is using
The resolvedOptions() method returns the actual options the collator is using, including the caseFirst setting.
const collator = new Intl.Collator('en-US', { caseFirst: 'upper' });
const options = collator.resolvedOptions();
console.log(options.caseFirst);
// Output: "upper"
This is useful for debugging or verifying that your collator has the expected configuration, especially when combining Unicode extensions with options objects.
const collator = new Intl.Collator('en-US-u-kf-lower');
const options = collator.resolvedOptions();
console.log(options.caseFirst);
// Output: "lower"
The resolved options reflect the Unicode extension setting when no options object overrides it.
When to use each caseFirst value
Use caseFirst: "upper" when:
- You want proper nouns or titles to appear before common words
- Your application convention treats capitalized text as more significant
- You are sorting names where capitalized versions are canonical
Use caseFirst: "lower" when:
- You want lowercase text to appear first as the standard form
- Your application convention treats lowercase as default and uppercase as special
- You are sorting technical identifiers where lowercase is more common
Use caseFirst: "false" when:
- You want to follow locale-specific conventions
- The order of case-differing strings does not matter to your application
- You want to make explicit that you are using defaults rather than accidentally omitting the option