NFC vs NFD: What Is the Difference?

NFC and NFD are Unicode normalization forms that represent canonically equivalent text using different code-point sequences.

On this page

NFC vs NFD at a glance

FeatureNFCNFD
Full nameNormalization Form CNormalization Form D
DecompositionCanonicalCanonical
Composition afterwardYesNo
Typical outputComposed where possibleDecomposed
Example for éU+00E9U+0065 U+0301
Canonically equivalentYesYes
Compatibility mappingsNoNo
Typical code-point countOften lowerOften higher
General-purpose storageCommon choiceUsed when decomposition is required
Visually identical outputUsuallyUsually

NFC favors composed canonical sequences. NFD favors decomposed canonical sequences. Both preserve canonical equivalence.

What is NFC?

NFC means Normalization Form C. It first performs canonical decomposition, then recomposes eligible sequences. In practice, it often replaces a base character plus combining mark with a precomposed character, such as e plus combining acute accent becoming é. It does not guarantee one code point per visible character, because many grapheme clusters have no single precomposed equivalent. It also preserves compatibility distinctions, so a ligature such as is not converted to ordinary fi.

Input:
U+0065 U+0301

NFC output:
U+00E9

Input:
U+0041 U+030A

NFC output:
U+00C5

NFC is commonly suitable for application text and interchange. Compare real strings with the Unicode Normalizer or read the broader Unicode Normalization Explained guide.

What is NFD?

NFD means Normalization Form D. It performs canonical decomposition and does not recompose eligible sequences. Precomposed characters may become a base character plus one or more combining marks. Like NFC, NFD preserves compatibility distinctions. It may increase code-point and UTF-8 byte counts, but it remains canonically equivalent to the corresponding NFC text. NFD can appear in filesystem behavior, linguistic analysis and workflows that need combining marks as separate code points.

Input:
U+00E9

NFD output:
U+0065 U+0301

Input:
U+00C5

NFD output:
U+0041 U+030A

NFD is not less valid or less correct than NFC. It is a different normalized representation.

How composition and decomposition work

Canonical decomposition

A precomposed character is replaced with its canonical component sequence when Unicode defines a decomposition mapping.

é → e + ◌́
U+00E9 → U+0065 U+0301

Canonical ordering

Combining marks are reordered according to canonical combining classes when required. Developers normally rely on runtime normalization libraries rather than reproducing the full algorithm.

Canonical composition

Eligible decomposed sequences are recomposed into precomposed characters.

e + ◌́ → é
U+0065 U+0301 → U+00E9

Composition exclusions and unavailable precomposed forms mean some sequences remain decomposed even after NFC.

NFC and NFD example with café

NFC

café
U+0063 U+0061 U+0066 U+00E9
UTF-8: 63 61 66 C3 A9

NFD

café
U+0063 U+0061 U+0066 U+0065 U+0301
UTF-8: 63 61 66 65 CC 81
PropertyNFCNFD
Grapheme clusters44
Code points45
UTF-8 bytes56
Visible textcafécafé
Direct equality without normalizationMay failMay fail

The rendered text may look identical despite different underlying data. Inspect the sequence with Unicode Character Inspector or Text to Unicode Code Points.

Interactive NFC and NFD comparison

This focused view reuses the same local normalization component used by the main normalization guide. It compares the original input with NFC and NFD, shows code points, grapheme counts and UTF-8 byte counts, and never sends entered text to the server.

NFC and NFD comparison

Compare composed and decomposed canonical forms side by side.

Processed locally in your browser

Limit: 2,000 UTF-16 code units. Text stays in your browser.

Open full Unicode Normalizer Open Unicode Normalization Checker
FormRendered textCode pointsCode-point countGrapheme clustersUTF-8 bytesMatches original?Changed sequences

Canonical equivalence

NFC and NFD forms can contain different code points while representing the same abstract text. U+00E9 and U+0065 U+0301 are canonically equivalent, but they are not byte-identical and are not raw-string-identical in most programming languages. Canonical equivalence is a Unicode relationship. Ordinary equality operators usually compare the actual stored sequence.

Canonical equivalence also does not imply compatibility equivalence. NFC and NFD do not convert compatibility characters such as ligatures, circled digits or full-width forms. Use NFKC or NFKD only under a documented policy. For the underlying text units, read What Is a Unicode Code Point? and Code Points vs Code Units.

NFC vs NFD code-point count

NFD often uses more code points because precomposed characters are decomposed. NFC may use fewer code points where composition is available. The difference is not universal: many characters have no decomposition, and some grapheme clusters remain multiple code points in NFC. Code-point count is not visible-character count.

TextNFC code pointsNFD code points
A11
é12
Å12
😀11
👨‍👩‍👧‍👦MultipleMultiple

NFC vs NFD byte size

Normalization can change encoded byte count. NFC is often smaller for characters with precomposed forms, while NFD may require additional code points and bytes. This does not mean NFC is always smaller, and compression, markup and surrounding content affect actual file size. Storage size should be measured in the actual encoding.

é in NFC:
U+00E9
UTF-8: C3 A9
2 bytes

é in NFD:
U+0065 U+0301
UTF-8: 65 CC 81
3 bytes

Both forms may also require different sizes in UTF-16. Check bytes with the UTF-8 Encoder and Decoder and Byte Length Calculator.

NFC vs NFD string equality

const nfc = "caf\u00E9";
const nfd = "cafe\u0301";

console.log(nfc === nfd); // false
console.log(
    nfc.normalize("NFC") === nfd.normalize("NFC")
); // true

Raw equality checks code-unit sequences. Normalize both operands to the same form; do not normalize only one value. Case-insensitive comparison, locale-aware collation and confusable-character checks are separate concerns. Use Unicode Text Compare or the Unicode Normalization Checker when debugging equality failures.

NFC and NFD are not encodings

NFC and NFD describe code-point normalization. UTF-8 and UTF-16 describe encoding. NFC text can be encoded as UTF-8 or UTF-16, and NFD text can also be encoded as UTF-8 or UTF-16. Converting UTF-16 to UTF-8 does not automatically change NFD to NFC, and normalizing text does not automatically change its character encoding.

Text
→ normalize as NFC or NFD
→ encode as UTF-8, UTF-16 or UTF-32
→ store or transmit bytes

For encoding background, read Unicode vs UTF-8 and UTF-8 vs UTF-16.

NFC and NFD do not remove accents

NFD may separate an accent into a combining mark, but it does not remove the mark. NFC may recombine the sequence. Accent removal requires an additional destructive operation, and not every accented character decomposes into a simple base Latin letter plus accent.

é
→ NFD
e + ◌́

Removing U+0301 would produce e, but that operation is not Unicode normalization. It can change meaning, spelling and names.

NFC vs NFD and grapheme clusters

NFC and NFD can have different code-point counts while preserving the same grapheme-cluster count. A decomposed base-plus-mark sequence is normally one grapheme cluster. NFC does not guarantee one code point per grapheme cluster; emoji and complex writing systems can remain multi-code-point clusters.

é in NFC:
1 grapheme cluster
1 code point

é in NFD:
1 grapheme cluster
2 code points

Read What Is a Grapheme Cluster?, then test text with Unicode Character Counter or Unicode Sequence Analyzer.

NFC and NFD in filenames

Filenames may look identical while using NFC or NFD-like sequences. Filesystems and operating systems can expose different normalization behavior, so applications must not assume every filename is stored in NFC. Raw string comparisons may fail, copying files between systems may reveal differences, and automatic renaming can create collisions.

résumé.txt

Each accented é might be represented in composed or decomposed form. File operations should preserve the actual filesystem-provided path, while display and comparison logic may use a separate normalized key.

NFC and NFD in databases

Databases may store NFC and NFD strings as distinct byte sequences. Unique constraints may or may not treat them as equivalent depending on collation, engine and configuration. Normalizing comparison keys can improve consistency, but original display text may need to be preserved separately. Existing data should be audited before introducing normalization, and indexes and unique constraints must use a consistent policy.

display_value
normalized_comparison_value

Normalization does not replace collation design. All writes and comparisons must apply the same rule.

NFC and NFD in URLs and slugs

URL encoding converts bytes, not normalization forms. NFC and NFD can produce different UTF-8 byte sequences, which produce different percent-encoded paths. Slug generation should define one normalization form, canonical URLs should use a consistent policy, and existing URLs should not be silently changed without redirects.

Normalize
→ apply slug rules
→ encode URL components

Original titles can be preserved while normalized values generate slugs. Use the URL Encoder and Decoder to inspect encoded URL components.

NFC and NFD in APIs

JSON strings can contain NFC or NFD text. Two values that look identical may fail direct equality, so API contracts should document normalization-sensitive identifiers. Normalization can be applied at defined boundaries, and producers and consumers must use compatible policies. Digital signatures and hashes are sensitive to byte differences, so do not normalize signed payloads unless the protocol defines it.

NFC vs NFD in JavaScript

const composed = "caf\u00E9";
const decomposed = "cafe\u0301";

console.log(composed.normalize("NFC"));
console.log(decomposed.normalize("NFC"));
console.log(composed.normalize("NFD"));
console.log(decomposed.normalize("NFD"));
function isNFC(text) {
    return text === text.normalize("NFC");
}

function isNFD(text) {
    return text === text.normalize("NFD");
}

function canonicallyEqual(first, second) {
    return first.normalize("NFC") === second.normalize("NFC");
}

normalize() returns a new string and leaves the original unchanged. Invalid form names throw an error. JavaScript equality does not automatically normalize, string length still counts UTF-16 code units, and NFC comparison is not case-insensitive comparison.

NFC vs NFD in Python

import unicodedata

composed = "caf\u00E9"
decomposed = "cafe\u0301"

nfc = unicodedata.normalize("NFC", decomposed)
nfd = unicodedata.normalize("NFD", composed)
def is_nfc(text: str) -> bool:
    return text == unicodedata.normalize("NFC", text)

def is_nfd(text: str) -> bool:
    return text == unicodedata.normalize("NFD", text)

def canonically_equal(first: str, second: str) -> bool:
    return (
        unicodedata.normalize("NFC", first)
        == unicodedata.normalize("NFC", second)
    )

Python strings represent Unicode text. Normalization changes the code-point sequence; encoding to UTF-8 is a separate operation. The Unicode data version follows the Python runtime, so tests should use the actual runtime data.

NFC vs NFD in PHP

$composed = "caf\u{00E9}";
$decomposed = "cafe\u{0301}";

$nfc = Normalizer::normalize(
    $decomposed,
    Normalizer::FORM_C
);

$nfd = Normalizer::normalize(
    $composed,
    Normalizer::FORM_D
);
$isNfc = Normalizer::isNormalized(
    $composed,
    Normalizer::FORM_C
);

$isNfd = Normalizer::isNormalized(
    $decomposed,
    Normalizer::FORM_D
);
function canonicallyEqual(
    string $first,
    string $second
): bool {
    $normalizedFirst = Normalizer::normalize($first, Normalizer::FORM_C);
    $normalizedSecond = Normalizer::normalize($second, Normalizer::FORM_C);

    if ($normalizedFirst === false || $normalizedSecond === false) {
        throw new RuntimeException("Unicode normalization failed.");
    }

    return $normalizedFirst === $normalizedSecond;
}

The Intl extension is required, input should be valid UTF-8 when expected, and Normalizer::normalize() may return false. PHP strings remain byte sequences, and NFC/NFD normalization is separate from mb_strlen() and grapheme operations.

When should you use NFC?

NFC is a likely starting point for general application text, web content, API comparison keys, search indexing, slug generation, database comparison values, deduplication, canonical equality checks and mixed-source imports. It is often the most practical canonical form for general-purpose text, but the application must define the policy explicitly.

NFC is not mandatory and should not automatically replace original text when exact representation matters.

When might NFD be useful?

NFD can be useful for linguistic analysis, processing combining marks separately, accent-related transformations, systems or file representations that expose decomposed text, algorithms requiring canonical decomposition, comparing canonical components and specialized text-processing pipelines.

NFD is useful when the application needs canonical components explicitly, not because it is universally more correct.

When should original text be preserved?

Preserve original text for legal or archival records, digital signatures, cryptographic hashes, exact user input, source-code analysis, forensic data, filesystem paths, external identifiers and synchronized data. A common pattern is to keep the source value and separately compute a normalized value for a defined comparison or indexing purpose.

Original value → preserve
Normalized value → use for defined comparison or indexing

Never normalize bytes after signing or hashing unless the protocol explicitly requires normalization first.

Common NFC and NFD mistakes

Assuming identical rendering means identical data

Code-point and byte sequences can differ.

Normalizing only one side of a comparison

Both sides need the same policy.

Assuming NFC produces one code point per character

Many grapheme clusters remain multi-code-point.

Assuming NFD removes accents

It decomposes many characters but does not remove combining marks.

Treating NFC as UTF-8

Normalization form and encoding are separate.

Treating NFD as broken text

NFD is a valid normalized representation.

Replacing original text without a reason

Preserve source data where fidelity matters.

Ignoring filename collisions

Normalization can make distinct raw names compare equally.

Normalizing after hashing or signing

Byte differences matter.

Using NFC as a security filter

It does not detect confusable scripts or invisible controls.

Assuming all operating systems use one normalization form

Filesystem behavior varies.

Confusing canonical equivalence with compatibility equivalence

NFC and NFD do not convert compatibility characters such as ligatures to ordinary letters.

Practical NFC/NFD workflow

  1. Preserve the original value when fidelity matters.
  2. Confirm that the input is valid Unicode text.
  3. Identify whether the operation is display, comparison, indexing or storage.
  4. Choose NFC or NFD deliberately.
  5. Normalize all compared values consistently.
  6. Handle case folding separately when required.
  7. Handle accent-insensitive behavior separately.
  8. Detect invisible and confusable characters separately.
  9. Store a normalized comparison key when appropriate.
  10. Test precomposed accents, combining marks, emoji and non-Latin scripts.

Use Unicode Normalizer, Unicode Normalization Checker, Unicode Text Compare and Unicode Character Inspector.

Which should you choose?

RequirementLikely choice
General web and application textNFC
Canonical comparison keyUsually NFC
Decomposed character analysisNFD
Processing combining marksNFD may help
Exact source preservationPreserve original
Filesystem operationsPreserve actual path representation
Search indexingOften NFC, according to product policy
Digital signaturesFollow the protocol exactly
Compatibility-insensitive comparisonNeither alone; consider NFKC under a documented policy

Choose NFC as the usual general-purpose canonical representation. Choose NFD when your processing explicitly requires decomposed canonical sequences. This is a practical starting point, not a universal rule.

Try these UnicodeNow tools

These tools help inspect normalization forms, byte counts, code points, invisible characters and equality behavior.

Unicode Normalizer

Normalize Unicode text to NFC, NFD, NFKC or NFKD.

NormalizationProcessed locally

Unicode Text Compare

Compare strings exactly and after Unicode normalization.

Text ComparisonProcessed locally

Unicode Character Inspector

Inspect each Unicode character, encoding, category, script and normalization form.

UnicodeProcessed locally

Unicode Sequence Analyzer

Analyze code points, grapheme clusters, bytes, scripts and directionality.

UnicodeProcessed locally

Unicode Character Counter

Count code points, grapheme clusters, words, bytes and invisible characters.

Text ComparisonProcessed locally

Frequently asked questions

What is the difference between NFC and NFD?

NFC recomposes canonical sequences where possible, while NFD keeps them canonically decomposed.

Are NFC and NFD visually identical?

They often render identically, but their code-point and byte sequences may differ.

Is NFC better than NFD?

Not universally. NFC is a common general-purpose form; NFD is useful when decomposed canonical sequences are required.

Is NFC the same as UTF-8?

No. NFC is a normalization form; UTF-8 is a character encoding.

Does NFD remove accents?

No. It may separate accents into combining marks, but it does not delete them.

Does NFC always use fewer code points?

Often for characters with precomposed forms, but not for every string.

Can NFC and NFD strings fail equality comparison?

Yes. Raw equality normally compares the stored sequence.

How can I compare NFC and NFD strings safely?

Normalize both values to the same form before comparison.

Does NFC make one code point per visible character?

No. Emoji, combining sequences and many scripts may remain multi-code-point grapheme clusters.

Can NFC and NFD have different UTF-8 byte lengths?

Yes. Decomposed text may require additional code points and bytes.

Should databases store NFC?

NFC is often a practical choice, but database collation, legacy data and application requirements must be considered.

Do filesystems always use NFD?

No. Filesystem and operating-system behavior varies, and applications should preserve actual filenames.

Can normalization change hashes or signatures?

Yes. Different normalized forms may produce different encoded bytes.

Does NFC prevent Unicode spoofing?

No. Confusables, mixed scripts and invisible controls require separate validation.

References