String.prototype.normalize()
normalize([form]) The normalize() method returns the Unicode normalization form of the string. This is essential for comparing strings that may look identical but have different character compositions—like characters with accents that can be represented as single characters or as a base character plus combining marks.
Syntax
str.normalize([form])
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
form | string | "NFC" | The Unicode normalization form: "NFC", "NFD", "NFKC", or "NFKD" |
Normalization Forms
- NFC (Canonical Decomposition, Canonical Composition) — Default. Composes characters where possible.
- NFD (Canonical Decomposition) — Decomposes characters into their base + combining marks.
- NFKC (Compatibility Decomposition, Canonical Composition) — Similar to NFC but also normalizes compatibility characters.
- NFKD (Compatibility Decomposition) — Most aggressive decomposition.
Examples
Basic normalization with NFC
const str1 = "\u00E9"; // é as single character
const str2 = "e\u0301"; // e + combining acute accent
console.log(str1 === str2);
// false — they're visually identical but different code points
console.log(str1.normalize() === str2.normalize());
// true — normalize() makes them equal
The basic NFC comparison handles strings that arrive with different Unicode representations. In practice, normalization is most useful at input boundaries — when you accept text from a user, a file, or an API — because you cannot control how the source encodes the characters. Normalizing at the point of entry means all downstream comparisons work consistently.
Comparing user input
function normalizeInput(input) {
return input.normalize("NFC");
}
const user1 = normalizeInput("café");
const user2 = normalizeInput("café");
console.log(user1 === user2);
// true — works even if user typed differently
NFC is the best default for comparison and storage because it produces compact, composed forms that most systems expect. But if you need to count individual character components — for example, to strip diacritics or analyze the underlying character structure — NFD is more useful. NFD decomposes combined characters so you can work with the pieces directly.
Using NFD for character analysis
const str = "résumé";
// NFC: keeps composed form
console.log(str.normalize("NFC").length);
// 6
// NFD: decomposes into base + combining marks
console.log(str.normalize("NFD").length);
// 8 (é becomes e + ́)
NFD is also useful when building search features that need to handle accents flexibly. Beyond single-character analysis, normalization often appears in combination with other string operations — for example, normalizing and lowercasing both the search term and the target before comparing them. This approach works for arrays, databases, and any collection of text you need to query.
Searching within normalized strings
const words = ["café", "resume", "naïve"];
// Normalize both the search term and array elements
const searchTerm = "CAFÉ".toLowerCase().normalize("NFC");
const found = words.find(w =>
w.normalize("NFC").toLowerCase() === searchTerm
);
console.log(found);
// "café"
Common Patterns
Form selection guidelines:
- Use NFC for most cases—it’s what users expect and matches most text formats
- Use NFD when you need to count or manipulate individual character components
- Use NFKC/NFKD when dealing with legacy compatibility characters (like ligatures)
Database storage: Always normalize before storing Unicode strings to ensure consistent matching.
Search/compare: Normalize both strings before comparison to handle user variations gracefully.
Why normalization matters for comparison
The same visible character can be encoded as different sequences of Unicode code points. An accented letter like é can be stored as U+00E9 (a single precomposed character) or as e + U+0301 (a combining acute accent). JavaScript’s === compares code points directly, so two visually identical strings may test as unequal:
"é" === "é" // false — different code points
"é".length // 1
"é".length // 2
// After NFC normalization, they become the same sequence
"é".normalize("NFC") === "é".normalize("NFC") // true
Any system that accepts text from multiple sources — user input forms, file imports, APIs — should normalize before comparing or storing.
NFC vs NFD in practice
NFC (the default) composes characters into the most compact representation. NFD decomposes them into base character plus combining marks. For most applications:
- Store and compare in NFC — it is compact and what most operating systems use natively
- Use NFD when you want to strip diacritics (accents) by removing combining characters after decomposition
// Strip accents using NFD + regex
function stripAccents(str) {
return str.normalize("NFD").replace(/[̀-ͯ]/g, "");
}
stripAccents("résumé"); // "resume"
stripAccents("naïve"); // "naive"
This pattern is useful for building search indexes where accent-insensitive matching is desired.
NFKC and NFKD for compatibility characters
The K forms additionally normalize compatibility characters — variant representations that look similar but have different code points, such as fullwidth ASCII (A vs A), Roman numerals (Ⅷ vs VIII), and ligatures (fi vs fi):
"first".normalize("NFKC"); // "first" — ligature fi decomposed
"A".normalize("NFKC"); // "A" — fullwidth to ASCII
Use NFKC when accepting input from sources that might use compatibility forms, such as copy-pasted content from PDFs or legacy databases.
See Also
- String.prototype.includes()
- String.prototype.slice()
- String.prototype.localeCompare() — Locale-aware string comparison