String.prototype.normalize()
Returns the Unicode Normalization Form of a string so equivalent spellings compare equal.
normalize()
normalize(form)- Added in JavaScript ES2015
Parameters
| Parameter | Type | Description |
|---|---|---|
$formoptional | "NFC" | "NFD" | "NFKC" | "NFKD"= "NFC" | The normalization form to apply. NFC is canonical decomposition followed by canonical composition; NFD is canonical decomposition; NFKC and NFKD are the compatibility equivalents. |
Return value
stringA new string containing the Unicode Normalization Form of the calling string.
Errors
Throws a RangeError if form is not one of "NFC", "NFD", "NFKC", or "NFKD".
Unicode can spell the same visible text more than one way. é may be the single code point U+00E9, or the letter e followed by U+0301 COMBINING ACUTE ACCENT. They render identically and === says they are different. normalize() rewrites a string into one canonical spelling so the comparison works.
Choosing a form
- NFC — composes to the shortest form. The default, and what you want for storage, keys, and equality.
- NFD — decomposes into base letters plus combining marks. Useful as a first step in stripping accents.
- NFKC / NFKD — additionally fold compatibility characters:
fibecomesfi,①becomes1, full-width forms become ASCII. Lossy, but good for search indexes.
When to reach for it
Normalise at the boundary — text pasted by a user, filenames from macOS (which hands out NFD), data from a third-party API — then compare and store the normalised value. Normalising on every comparison instead invites the bug where a stored key and a fresh input differ invisibly.
The classic accent-stripper is str.normalize("NFD").replace(/\p{Diacritic}/gu, ""). It works because NFD splits the mark off as its own code point.
Example
When to use it
- Reconcile a filename read from macOS with the same name typed by a user
- Build accent-insensitive search keys by normalizing to NFD and stripping diacritics
- Fold full-width and ligature characters before indexing text with NFKC
- De-duplicate names pasted from different sources that look identical
Frequently asked questions
Why do two identical-looking strings compare as unequal in JavaScript?
They are almost certainly different Unicode spellings of the same text — a precomposed accented letter versus a base letter plus a combining mark. Call normalize("NFC") on both before comparing, and store the normalized form so the mismatch cannot recur.
What is the difference between NFC and NFD?
NFC composes characters into the shortest precomposed form and is the right default for storage and equality, while NFD decomposes them into base characters plus combining marks. Reach for NFD when you want to inspect or remove the marks themselves.
How do I remove accents from a string in JavaScript?
Use str.normalize("NFD").replace(/\p{Diacritic}/gu, "") — NFD splits each accent into its own code point and the Unicode-aware regex deletes them. This handles Latin scripts well but does not transliterate other writing systems.
More examples
Stripping accents via NFD
NFD separates each accent into its own combining code point, which the regex then deletes. All three spellings of the name collapse to one search key, so the Set reports a single entry. Cyrillic is unaffected because those letters carry no separable marks.
const strip = (s) => s.normalize("NFD").replace(/\p{Diacritic}/gu, "");
console.log(strip("café"));
console.log(strip("Ñandú"));
console.log(strip("Дежавю"));
const names = ["Zoë", "Zoe", "Zoé"];
console.log(new Set(names.map((n) => strip(n).toLowerCase())).size);Compatibility forms fold more aggressively
NFC leaves the fi ligature intact because the two spellings are not canonically equivalent, while NFKC rewrites it as plain "fi". Circled digits and full-width Latin also fold to ASCII. An unrecognised form name is a RangeError, not a silent fallback to NFC.
console.log("fi".normalize("NFC"), "|", "fi".normalize("NFKC"));
console.log("①".normalize("NFKC"));
console.log("Hello".normalize("NFKC"));
try {
"x".normalize("NFX");
} catch (err) {
console.log(err.name);
}
Discussion