Unicode NFC / NFD normalizer

Normalize Unicode text in your browser with UAX #15 NFC, NFD, NFKC, and NFKD. Compose or decompose characters locally. Nothing uploaded.

Unicode normalization converts text to a standard form so that canonically equivalent characters have identical byte sequences. NFC composes characters (e + combining acute → é), while NFD decomposes them (é → e + combining acute). Use NFKC or NFKD to also expand compatibility characters like ligatures (fi → fi). This tool uses String.prototype.normalize per UAX #15. Nothing is uploaded.

Unicode text

Code points appear after you type.

NFC text

Code points appear after Normalize.

How it works

  1. Choose a normalization form. NFC composes characters into precomposed form (preferred for storage and interchange). NFD decomposes them. NFKC and NFKD also apply compatibility decomposition (ligatures, width variants).
  2. Click Normalize. The tool calls String.prototype.normalize with your chosen form. This is ECMA-262's binding of UAX #15. Mapping tables come from the browser.
  3. View and download results. See the normalized text with its Unicode code points. Download saves the result as normalized.txt.

NFC vs NFD vs NFKC

This page runs one UAX #15 form at a time through String.prototype.normalize. NFC is canonical decomposition, then composition. NFD is canonical decomposition only. NFKC and NFKD also apply compatibility mappings (the K), then NFKC composes. Node and CPython expose the same four form names. ICU uconv lists them as Any-NFC, Any-NFD, Any-NFKC, and Any-NFKD. For the longer comparison, see NFC vs NFD vs NFKC: Canonical vs Compatibility.

AspectNFCNFDNFKC / NFKD
AlgorithmDecompose, then composeDecompose onlyCompatibility mappings; NFKC then composes
AppearanceShould match NFDShould match NFCMay change (ligatures, width variants)
e + U+0301é (U+00E9)Stays U+0065 U+0301NFKC U+00E9; NFKD stays
é (U+00E9)Stayse + U+0301Same as NFC / NFD
fi (U+FB01)Stays U+FB01Stays U+FB01fi (U+0066 U+0069)
Typical useStorage and interchangeProcess combining marksSearch and compare

The ångström sign Å (U+212B) is a canonical singleton: NFC writes Å (U+00C5); NFD of that composed letter is A + U+030A. ASCII hello stays hello. This page does not lowercase a host or add xn--. IDNA of NFC é and of NFD e + U+0301 is xn--9ca on IDNA ToASCII. Punycode of NFC é is 9ca; Punycode of that NFD sequence is e-xbb on Punycode because that page never calls String.prototype.normalize.

Unicode normalize in Python and Node

The snippets below apply UAX #15 NFC, NFD, NFKC, and NFKD — the same four forms this page uses via String.prototype.normalize. They do not lowercase, add an xn-- prefix, or encode Punycode. ICU uconv samples are in Unicode normalize in the shell below. For when to pick a form, see NFC vs NFD vs NFKC: Canonical vs Compatibility.

Node.js with String.prototype.normalize

Node uses the same ECMA-262 method as this page. Omitting the argument defaults to NFC. Invalid forms throw RangeError. The code-point lists below match this page's U+ display:

const nfc = 'e\u0301'.normalize('NFC');
// U+00E9

const nfd = '\u00E9'.normalize('NFD');
// U+0065 U+0301

const nfkc = '\uFB01'.normalize('NFKC');
// U+0066 U+0069  (fi). NFC leaves U+FB01 unchanged.

Python with unicodedata.normalize

CPython unicodedata.normalize requires the form argument. Mapping tables come from unicodedata.unidata_version and can differ from the browser's tables. Invalid forms raise ValueError. is_normalized (Python 3.8+) is a check only:

import unicodedata

nfc = unicodedata.normalize('NFC', 'e\u0301')
# U+00E9

nfd = unicodedata.normalize('NFD', '\u00E9')
# U+0065 U+0301

nfkc = unicodedata.normalize('NFKC', '\uFB01')
# U+0066 U+0069  (fi). NFC leaves U+FB01 unchanged.

unicodedata.is_normalized('NFC', 'e\u0301')  # False

The ångström sign U+212B is canonically equivalent to U+00C5, so NFC writes U+00C5 in Node, CPython, and this page. Compatibility forms can change appearance (ligatures, fullwidth digits). This is not IDNA or Punycode.

Unicode normalize in the shell

There is no POSIX UAX #15 converter. ICU uconv (Debian / Ubuntu icu-devtools) applies Unicode normalization as a transliterator — the same four forms this page uses via String.prototype.normalize. On the documented sequence (e + U+0301), uconv -x any-nfc writes the same U+00E9 Normalize writes. The listed names are Any-NFC, Any-NFD, Any-NFKC, and Any-NFKD (uconv -L). Short nfc / nfd / nfkc / nfkd are the same. glibc iconv is charset conversion, not UAX #15. uconv is not POSIX. For the Node and CPython snippets, see Unicode normalize in Python and Node above.

ICU uconv (Linux)

Confirmed here with uconv v2.1 (ICU 74.2). -x runs the transliterator. Use printf, not echo. The $'\uXXXX' escapes are bash ANSI-C quoting, not POSIX printf:

# NFC of e + combining acute (same U+00E9 as this page)
printf '%s' $'e\u0301' | uconv -x any-nfc
# é

# NFD of precomposed é
printf '%s' $'\u00E9' | uconv -x any-nfd
# e + U+0301

# NFKC of the fi ligature (U+FB01 → fi). NFC leaves U+FB01
printf '%s' $'\uFB01' | uconv -x any-nfkc
# fi

printf '%s' $'\uFB01' | uconv -x any-nfc
# fi

# Canonical singleton: ångström sign → Å
printf '%s' $'\u212B' | uconv -x any-nfc
# Å

uconv does not add a trailing newline; it keeps one when the input has one. A bash here-string (<<<) always appends a newline, so uconv -x any-nfc <<< $'e\u0301' writes U+00E9 U+000A. Empty stdin exits 0 with no output; Normalize stays disabled until there is text. Without -x, uconv is a charset converter — UTF-8 to UTF-8 leaves e + U+0301 decomposed. Invalid -x names exit 1 with Couldn't create transliteration "…": U_INVALID_ID. Any-FCC and Any-FCD are not the four UAX #15 forms this page runs; FCD left the ångström sign as U+212B. Latin-ASCII is a different transliterator (it stripped the acute from e + U+0301 to e). Leading or trailing spaces are not trimmed. Fullwidth U+FF11 becomes ASCII 1 under NFKC and stays under NFC — the same split as this page. --add-signature / --remove-signature toggle a BOM; they are not normalization. This is not IDNA or Punycode.

FAQ

Does this tool upload my text?
No. All normalization runs in your browser using String.prototype.normalize. Your text is never sent to a server.
What is the difference between NFC and NFD?
NFC (Canonical Composition) composes characters into their precomposed form — e + combining acute (U+0301) becomes é (U+00E9). NFD (Canonical Decomposition) does the opposite — é becomes e + U+0301. Both forms are canonically equivalent and should render identically, but they differ at the byte level.
When should I use NFKC or NFKD instead?
NFKC and NFKD apply compatibility decomposition in addition to canonical normalization. They convert ligatures like fi (U+FB01) to fi, fullwidth digits to ASCII, and superscripts to regular characters. Use them when you need searchable or comparable text, but note that the original representation is lost.
Is this IDNA, Punycode, or a confusables checker?
No. This tool only performs UAX #15 normalization. It does not lowercase, split DNS labels, add the xn-- prefix, percent-encode, or detect visually similar characters. For internationalized domain names, see the IDNA tool. For raw Punycode encoding, see the Punycode tool.

Related