jsguides

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

ParameterTypeDefaultDescription
formstring"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 ( 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