JavaScript String normalize() Method

Beginner
⏱️ 11 min read
📚 Updated: Jul 2026
🎯 5 Examples
🚀 5 Try-it labs
Instance method

What You’ll Learn

String.prototype.normalize() returns the Unicode Normalization Form of a string (same API as MDN String.prototype.normalize()). Learn why lookalike strings can fail ===, the four forms (NFC, NFD, NFKC, NFKD), composed vs decomposed accents, compatibility ligatures, five examples, and try-it labs.

01

Kind

Instance method

02

Returns

New string

03

Default

NFC

04

Forms

NFC / NFD / NFKC / NFKD

05

Mutates?

No

06

Baseline

Widely available

Introduction

Unicode sometimes has more than one way to store the same character. The letter "é" can be one code point, or "e" plus a combining accent. Both look like Amélie — but === says they are different, and even length can differ.

normalize() converts a string into a shared form so equivalent sequences compare equal. Pick a form once (usually "NFC") and use it before comparing, sorting keys, or storing text.

💡
Beginner tip

Normalize both sides before comparing: a.normalize() === b.normalize(). Normalizing only one side is a common bug.

This page is part of JavaScript String Methods. Related topics include isWellFormed() and localeCompare().

Understanding the normalize() Method

Calling str.normalize(form) returns a new string in the chosen Unicode Normalization Form. It does not edit str in place.

  • NFC / NFD — canonical equivalence (same abstract character, same appearance).
  • NFKC / NFKD — compatibility equivalence (treat ligatures / fancy symbols alike for some apps).
  • C forms are composed; D forms are decomposed.
  • Invalid form — throws RangeError.

📝 Syntax

General forms of String.prototype.normalize:

JavaScript
str.normalize()
str.normalize(form)

Parameters

  • form (optional)"NFC", "NFD", "NFKC", or "NFKD". If omitted or undefined, "NFC" is used.

Return value

A string in the requested normalization form:

FormMeaning
"NFC" (default)Canonical Decomposition, then Canonical Composition
"NFD"Canonical Decomposition (base + combining marks)
"NFKC"Compatibility Decomposition, then Canonical Composition
"NFKD"Compatibility Decomposition
Invalid formThrows RangeError

Common patterns

JavaScript
const a = "\u00e9";           // é as one code point
const b = "e\u0301";          // e + combining acute

a === b;                      // false
a.normalize() === b.normalize(); // true  (both NFC)

// Stable compare helper:
function sameText(x, y) {
  return x.normalize("NFC") === y.normalize("NFC");
}

⚡ Quick Reference

GoalCode
Default (NFC)str.normalize()
Compare safelya.normalize() === b.normalize()
Decompose accentsstr.normalize("NFD")
Compatibility (search)str.normalize("NFKC")
Strip combining marks*str.normalize("NFD").replace(/\p{M}+/gu, "")
Invalid formRangeError

*Advanced pattern — requires Unicode property escapes in the regex.

🔍 At a Glance

Four facts to remember about String.normalize().

Returns
string

New normalized text

Default
NFC

Composed canonical

Best for
=== / keys

Before comparing

Mutates
no

Strings stay immutable

📋 normalize() vs localeCompare() vs raw ===

normalize()===localeCompare()
PurposeRewrite to a Unicode formExact code-unit equalityLocale-aware sort order
Fixes composed/decomposed?Yes (after normalizing both)NoOften treats them alike for sorting
ResultNew stringBooleanNumber (<0 / 0 / >0)
Best forStable keys & equalityExact same bytes/unitsHuman-facing sort

Examples Gallery

Examples follow MDN String.normalize() patterns. Use View Output or Try It Yourself for each case.

📚 Getting Started

See why lookalike strings differ — and how NFC fixes them.

Example 1 — Basic NFC Equality

MDN demo: two spellings of Amélie become equal after NFC.

JavaScript
const name1 = "\u0041\u006d\u00e9\u006c\u0069\u0065"; // Amélie (composed é)
const name2 = "\u0041\u006d\u0065\u0301\u006c\u0069\u0065"; // Amélie (e + accent)

name1 === name2;           // false
name1.length === name2.length; // false (6 vs 7)

const name1NFC = name1.normalize("NFC");
const name2NFC = name2.normalize("NFC");

name1NFC === name2NFC;     // true
name1NFC.length === name2NFC.length; // true
Try It Yourself

How It Works

Both strings display the same, but one uses a precomposed é and the other uses e + combining acute. NFC brings them to the same composed form.

Example 2 — NFC vs NFD for "ñ"

MDN-style: composed single code point vs n + tilde.

JavaScript
const string1 = "\u00F1";       // ñ
const string2 = "\u006E\u0303"; // n + combining tilde

string1 === string2;  // false
string1.length;       // 1
string2.length;       // 2

string1.normalize("NFD") === string2.normalize("NFD"); // true (both length 2)
string1.normalize("NFC") === string2.normalize("NFC"); // true (both length 1)

string2.normalize("NFC").codePointAt(0).toString(16); // "f1"
Try It Yourself

How It Works

NFD prefers the decomposed pair; NFC prefers the single precomposed character. Either works for equality — stay consistent.

📈 Practical Patterns

Compatibility forms, all four modes, and invalid input.

Example 3 — Compatibility: Ligature "ff""ff"

MDN demo: NFKD makes a ligature compare equal to two fs.

JavaScript
let string1 = "\uFB00";         // ff ligature
let string2 = "\u0066\u0066";   // ff

string1 === string2;            // false
string1.length;                 // 1
string2.length;                 // 2

string1 = string1.normalize("NFKD");
string2 = string2.normalize("NFKD");

string1;                        // "ff"  (appearance changed)
string1 === string2;            // true
string1.length;                 // 2
Try It Yourself

How It Works

Compatibility normalization is great for search (“find ff”), but it can change how text looks. Prefer NFC/NFD when you must preserve appearance.

Example 4 — All Four Forms on One String

MDN sample: long s with dots under different forms.

JavaScript
// U+1E9B LATIN SMALL LETTER LONG S WITH DOT ABOVE
// U+0323 COMBINING DOT BELOW
const str = "\u1E9B\u0323";

str.normalize("NFC");   // same as str.normalize()
str.normalize("NFD");   // long s + dot below + dot above
str.normalize("NFKC");  // single compatibility character U+1E69
str.normalize("NFKD");  // plain s + combining dots

// Inspect code points (hex):
[...str.normalize("NFD")].map((c) => c.codePointAt(0).toString(16));
// ["17f", "323", "307"]

str.normalize("NFKC").codePointAt(0).toString(16); // "1e69"
Try It Yourself

How It Works

Canonical forms keep the “same character” identity. Compatibility forms may map to simpler letters (here, toward a plain s family).

Example 5 — Invalid Form Throws RangeError

Only the four official form names are allowed.

JavaScript
"hello".normalize();        // "hello" (NFC)
"hello".normalize("NFC");   // "hello"

try {
  "hello".normalize("XYZ");
} catch (e) {
  console.log(e.name);      // RangeError
}
Try It Yourself

How It Works

Typo-prone form names fail fast. Stick to the four uppercase strings from the Unicode / ECMAScript specs.

🚀 Common Use Cases

  • Equality checks — normalize both strings before ===.
  • Database / cache keys — store NFC so the same word always hashes the same.
  • Search indexing — NFKC/NFKD to fold ligatures and fancy symbols.
  • Form input cleanup — normalize user text on save.
  • Not a substitute for locale sort — still use localeCompare for language order.
  • Not the same as isWellFormed — that checks lone surrogates, not accents.

🧠 How normalize() Rewrites Text

1

Choose a form

Default NFC, or pass NFD / NFKC / NFKD.

Input
2

Decompose (and maybe recompose)

Canonical or compatibility rules expand and/or merge code points.

Transform
3

Produce a stable sequence

Equivalent inputs map to the same output for that form.

Unify
4

Return a new string

Compare, store, or index the normalized result.

📝 Notes

  • Default form is "NFC" when you omit the argument.
  • Normalize both sides before equality checks.
  • NFC/NFD preserve visual identity for canonical pairs; NFKC/NFKD may change appearance.
  • Invalid form throws RangeError.
  • This is unrelated to isWellFormed() / lone surrogates.
  • For human sort order across languages, still prefer localeCompare.

Browser & Runtime Support

String.prototype.normalize() is Baseline Widely available — supported across modern browsers since September 2016.

Baseline · Widely available

String.normalize()

Safe for production text equality and storage keys. Prefer NFC unless you have a specific reason to use another form.

Universal Widely available
Google Chrome Supported · Desktop & Mobile
Full support
Mozilla Firefox Supported · Desktop & Mobile
Full support
Apple Safari Supported · macOS & iOS
Full support
Microsoft Edge Supported · Chromium
Full support
Internet Explorer No native support · Use a polyfill
Polyfill
Opera Supported · Modern versions
Full support
Samsung Internet Supported · Android
Full support
Bun Supported · JavaScript runtime
Supported
Deno Supported · JavaScript runtime
Supported
Node.js Supported · Server runtime
Supported
Android WebView Supported · Modern WebView
Full support
normalize() Excellent

Bottom line: Use String.normalize() to unify composed and decomposed Unicode text. Normalize both sides before comparing, and choose NFC for most equality and storage use cases.

Conclusion

String.prototype.normalize() rewrites a string into a Unicode Normalization Form so equivalent characters compare and store consistently. Start with NFC, use NFD when you need decomposition, and reach for NFKC/NFKD only when compatibility folding is intentional.

Continue with isWellFormed(), localeCompare(), or the String methods hub.

💡 Best Practices

✅ Do

  • Normalize both strings before ===
  • Prefer NFC for storage and equality by default
  • Document which form your API expects
  • Use NFKC/NFKD intentionally for search folding
  • Catch RangeError if form comes from user input

❌ Don’t

  • Normalize only one side of a comparison
  • Assume lookalike text has the same length
  • Use NFKC for display when appearance must stay fancy
  • Confuse this with isWellFormed()
  • Pass typos like "nfc" / "XYZ" as the form

Key Takeaways

Knowledge Unlocked

Five things to remember about String.normalize()

Unicode forms that make equivalent text compare equal.

5
Core concepts
🌐 02

Default

NFC

Form
🔀 03

Problem

composed ≠ decomposed

Unicode
04

Compat

NFKC / NFKD

Search
📄 05

Mutates

no

Immutable

❓ Frequently Asked Questions

String.prototype.normalize(form?) returns a new string in a Unicode Normalization Form (NFC, NFD, NFKC, or NFKD). The default form is NFC. It does not change the original string.
Unicode can represent the same character as one code point (composed) or as a base letter plus combining marks (decomposed). They look the same but have different code points and lengths until you normalize them.
Both are canonical forms. NFD decomposes characters into base + combining marks. NFC decomposes then recomposes into precomposed characters where possible. Use either consistently before comparing.
For compatibility equivalence — for example treating the ligature ff like "ff", or circled letters like plain letters. Useful for search; be careful for display because appearance can change.
normalize() throws RangeError if form is not one of "NFC", "NFD", "NFKC", or "NFKD".
No. Strings are immutable. normalize() returns a new normalized string.
Did you know?

File systems, browsers, and keyboards may emit different Unicode forms for the same typed accented letter. That is why a filename or username that “looks right” can still fail a strict === check until you normalize.

More String Methods

Return to the hub for slice, trim, replace, and more.

String methods hub →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

8 people found this page helpful