Code Points vs Code Units: What Is the Difference?

A code point identifies a Unicode value, while a code unit is a storage unit used by an encoding such as UTF-8, UTF-16 or UTF-32.

On this page

Code points vs code units at a glance

ConceptCode pointCode unit
MeaningNumeric value in UnicodeSmallest storage unit used by an encoding
ExampleπŸ˜€ is U+1F600UTF-16 uses D83D and DE00
Depends on encodingNoYes
SizeAbstract number8, 16 or 32 bits depending on encoding
One per visible characterNot alwaysNot always
Common notationU+1F600Hex bytes or code-unit values

A code point answers β€œWhich Unicode value is this?” A code unit answers β€œHow does this encoding represent it?”

What is a Unicode code point?

A Unicode code point is an abstract numeric value in the Unicode codespace. It is commonly written as U+ followed by hexadecimal digits. Code points are independent of UTF-8, UTF-16 and UTF-32. A code point may represent a letter, symbol, punctuation mark, control, combining mark or emoji, and the same value has different code-unit representations in different encodings. For the broader standard, read What Is Unicode?.

CharacterUnicode nameCode point
ALATIN CAPITAL LETTER AU+0041
Γ©LATIN SMALL LETTER E WITH ACUTEU+00E9
Π–CYRILLIC CAPITAL LETTER ZHEU+0416
ε­—CJK UNIFIED IDEOGRAPH-5B57U+5B57
πŸ˜€GRINNING FACEU+1F600

For a deeper definition, read What Is a Unicode Code Point?, inspect text with the Unicode Character Inspector, or search by value with Unicode Character Lookup.

What is a code unit?

A code unit is the smallest unit used by a Unicode encoding form. UTF-8 uses 8-bit code units, UTF-16 uses 16-bit code units and UTF-32 uses 32-bit code units. One code point can require one or more code units, so code-unit count depends on the encoding.

EncodingCode-unit sizeCode units per code point
UTF-88 bits1–4
UTF-1616 bits1–2
UTF-3232 bits1

One code point in UTF-8, UTF-16 and UTF-32

Character: πŸ˜€

Code point: U+1F600

UTF-8 code units: F0 9F 98 80

UTF-16 code units: D83D DE00

UTF-32 code unit: 0001F600

The code point is unchanged, but the code-unit sequence differs. UTF-8 uses bytes as code units. UTF-16 uses a surrogate pair for this supplementary code point. UTF-32 uses one 32-bit code unit.

CharacterCode pointUTF-8 code unitsUTF-16 code unitsUTF-32 code unit
AU+004141004100000041
Γ©U+00E9C3 A900E9000000E9
Π–U+0416D0 96041600000416
ε­—U+5B57E5 AD 975B5700005B57
πŸ˜€U+1F600F0 9F 98 80D83D DE000001F600

Read Unicode vs UTF-8, compare UTF-8 vs UTF-16, or convert bytes with the UTF-8 Encoder and Decoder.

Interactive code point and code unit analyzer

This local analyzer shows one row per code point and summarizes grapheme clusters plus UTF-8, UTF-16 and UTF-32 units.

Code point and code unit analyzer

Compare code points, UTF-8 code units, UTF-16 code units and grapheme clusters.

Processed locally in your browser

Limit: 2,000 UTF-16 code units. Input is never sent to the backend, analytics or logs.

Character Unicode name Code point UTF-8 code units UTF-8 code-unit count UTF-16 code units UTF-16 code-unit count UTF-32 code unit Grapheme-cluster index

Why UTF-8 uses multiple code units

UTF-8 is variable-length. ASCII-range code points use one byte. Higher values use two, three or four bytes. Each byte is one UTF-8 code unit. The leading byte identifies sequence length, and continuation bytes carry additional bits. Invalid byte sequences should not be interpreted as valid code points.

Code-point rangeUTF-8 code units
U+0000–U+007F1
U+0080–U+07FF2
U+0800–U+FFFF3
U+10000–U+10FFFF4
A β†’ U+0041 β†’ 41
Γ© β†’ U+00E9 β†’ C3 A9
ε­— β†’ U+5B57 β†’ E5 AD 97
πŸ˜€ β†’ U+1F600 β†’ F0 9F 98 80

Why UTF-16 uses surrogate pairs

UTF-16 uses 16-bit code units. BMP code points usually use one code unit. Supplementary code points require two code units: a high surrogate and a low surrogate. Surrogates are reserved code-point values, and isolated surrogates are not valid Unicode scalar values.

πŸ˜€
Code point: U+1F600
UTF-16 code units: D83D DE00

D83D β†’ high surrogate
DE00 β†’ low surrogate

Use the Unicode Sequence Analyzer when surrogate pairs or code-unit counts look surprising.

Why UTF-32 uses one code unit per code point

UTF-32 code units are 32 bits, so every Unicode scalar value fits in one UTF-32 code unit. This makes code-point indexing simpler, but UTF-32 usually uses more storage and byte order matters when serialized.

A β†’ 00000041
Γ© β†’ 000000E9
πŸ˜€ β†’ 0001F600

Code points vs bytes

A byte is always 8 bits. A code point is an abstract Unicode number. In UTF-8, code units are bytes. In UTF-16 and UTF-32, code units contain multiple bytes, and byte count depends on encoding and byte order.

CharacterCode pointUTF-8 bytesUTF-16BE bytesUTF-16LE bytes
AU+00414100 4141 00
Γ©U+00E9C3 A900 E9E9 00
πŸ˜€U+1F600F0 9F 98 80D8 3D DE 003D D8 00 DE

U+00E9 is the code point. C3 A9 is its UTF-8 byte sequence.

Code points vs grapheme clusters

A grapheme cluster is a user-perceived character. One grapheme cluster may contain one or several code points. Combining marks, emoji modifiers and zero-width joiners create multi-code-point graphemes, and code-unit counts can be even larger.

Precomposed letter

Γ©
U+00E9
1 grapheme cluster
1 code point
2 UTF-8 code units
1 UTF-16 code unit

Decomposed letter

e + β—ŒΜ
U+0065 U+0301
1 grapheme cluster
2 code points
3 UTF-8 code units
2 UTF-16 code units

Family emoji

πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦ may display as one grapheme while containing several code points and many code units.

Use the Unicode Character Counter, Unicode Sequence Analyzer and Unicode Normalizer.

Why string length can be misleading

Programming languages and databases count different units, so the word β€œlength” is incomplete unless the unit is named.

Environment or operationCommonly counts
JavaScript String.lengthUTF-16 code units
JavaScript [...text].lengthCode points
JavaScript Intl.SegmenterGrapheme clusters
Python len(text)Code points in normal Python strings
PHP strlen()Bytes
PHP mb_strlen(..., "UTF-8")Encoding-aware characters/code points
Database byte-length functionBytes
Database character-length functionCharacters according to database semantics

Exact behavior depends on language, runtime and API. Code-point count is not universally equivalent to visible-character count.

Code points and code units in JavaScript

JavaScript strings are sequences of UTF-16 code units. charCodeAt() returns one UTF-16 code unit, codePointAt() can return a full code point, for...of iterates code points, and Intl.Segmenter is needed for grapheme-aware segmentation. Spread syntax is not grapheme-aware.

const text = "πŸ˜€";

console.log(text.length);       // 2 UTF-16 code units
console.log([...text].length);  // 1 code point
const emoji = "πŸ˜€";

console.log(emoji.charCodeAt(0).toString(16)); // d83d
console.log(emoji.charCodeAt(1).toString(16)); // de00
console.log(emoji.codePointAt(0).toString(16)); // 1f600
for (const character of "AπŸ˜€") {
    console.log(character, character.codePointAt(0));
}

const segmenter = new Intl.Segmenter("en", {
    granularity: "grapheme",
});

const count = [...segmenter.segment("πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦")].length;

Code points and code units in Python

Python strings represent Unicode text. Iteration usually yields code points, and len() counts string elements/code points in normal Python builds. Encoding produces byte sequences. Grapheme clusters still require a segmentation library.

text = "AπŸ˜€"

print(len(text))  # 2 code points

for character in text:
    print(character, f"U+{ord(character):04X}")
encoded = text.encode("utf-8")
print(list(encoded))

utf16 = "πŸ˜€".encode("utf-16-le")
units = [
    int.from_bytes(utf16[index:index + 2], "little")
    for index in range(0, len(utf16), 2)
]

print([f"{unit:04X}" for unit in units])

Code points and code units in PHP

PHP strings are byte sequences. strlen() counts bytes, mb_strlen() performs encoding-aware length calculation, and IntlChar::ord() can retrieve a code point. Unicode-safe iteration requires suitable extensions such as mbstring and intl.

$text = "AπŸ˜€";

echo strlen($text);
echo mb_strlen($text, "UTF-8");
foreach (mb_str_split($text, 1, "UTF-8") as $character) {
    $codepoint = IntlChar::ord($character);
    printf("%s U+%04X\n", $character, $codepoint);
}

Code points and code units in databases

Databases may expose character length, byte length, column storage limits, index limits, collation and normalization behavior. UTF-8-compatible character sets can store full Unicode, but byte limits can reject strings with few visible characters.

A 10-character field may require more than 10 bytes when stored as UTF-8.

User-facing character limits should not rely only on byte count, and normalization can affect uniqueness comparisons. Database-specific behavior varies, so check the actual database documentation.

Safe string truncation

Truncation is difficult because unsafe approaches can cut UTF-8 at an arbitrary byte offset, cut UTF-16 between surrogate code units, cut after a base character before a combining mark, split an emoji sequence or break a zero-width-joiner sequence.

RequirementCorrect unit
Network or storage byte limitBytes
Unicode scalar limitCode points
User-visible character limitGrapheme clusters
JavaScript internal index compatibilityUTF-16 code units
Database field limitDepends on database definition

Common code point and code unit mistakes

Treating JavaScript length as visible characters

It counts UTF-16 code units, so supplementary characters count as two.

Treating UTF-8 bytes as characters

Non-ASCII code points can require multiple bytes.

Splitting surrogate pairs

Broken pairs can produce invalid or replacement output.

Counting code points instead of grapheme clusters

Combining marks and emoji sequences can make several code points one visible character.

Assuming UTF-32 solves character counting

UTF-32 is fixed for code points, not graphemes.

Using byte limits as user-facing character limits

Multilingual text and emoji make byte length differ from visible length.

Confusing code units with code points in APIs

Document whether offsets and lengths are bytes, UTF-16 indexes, code-point indexes or grapheme indexes.

Mixing indexes from different representations

An index from one representation cannot safely be reused in another without conversion.

Confusing normalization with encoding

Normalization changes code-point sequences while encoding changes code points into code units. Read Unicode Normalization Explained before normalizing production identifiers.

Assuming visually identical text has identical code units

Normalization and invisible characters can create different sequences.

How to choose the right counting unit

Use bytes for file-size limits, network payload limits, binary formats and encoded storage. Use code units when an API is defined in UTF-16 indexes or when working directly with an encoding representation. Use code points for Unicode values, scalar validation and property logic. Use grapheme clusters for visible limits, cursor movement, deletion, display names and text editors.

TaskRecommended unit
Count UTF-8 storageBytes/code units
Inspect Unicode valuesCode points
JavaScript substring indexUTF-16 code units, unless using a higher-level method
User-visible character countGrapheme clusters
Validate API payload sizeBytes
Compare normalization sequencesCode points
Render text cursor movementGrapheme clusters

Practical debugging workflow

  1. Preserve the original text and bytes.
  2. Determine the declared encoding.
  3. Inspect code points.
  4. Inspect code units in the relevant encoding.
  5. Count grapheme clusters.
  6. Check normalization.
  7. Detect invisible and combining characters.
  8. Check for isolated UTF-16 surrogates.
  9. Compare byte and character limits.
  10. Test with emoji and non-Latin scripts.

Use Unicode Sequence Analyzer, Unicode Character Inspector, Unicode Character Counter, UTF-8 Encoder and Decoder, Unicode Text Compare and Invisible Character Detector.

Try these UnicodeNow tools

These tools help inspect code points, code units, byte length, normalization and visible counts in real input.

Unicode Sequence Analyzer

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

UnicodeProcessed locally

Unicode Character Inspector

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

UnicodeProcessed locally

Unicode Character Counter

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

Text ComparisonProcessed locally

Unicode Text Compare

Compare strings exactly and after Unicode normalization.

Text ComparisonProcessed locally

Unicode Normalizer

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

NormalizationProcessed locally

Byte Length Calculator

Count UTF-8 bytes, code points, grapheme clusters and UTF-16 code units for text.

EncodingProcessed locally

Frequently asked questions

What is the difference between a code point and a code unit?

A code point is a Unicode value. A code unit is a storage unit used by a specific encoding.

Can one code point use multiple code units?

Yes. For example, πŸ˜€ uses four UTF-8 code units and two UTF-16 code units.

Is a UTF-8 byte the same as a code unit?

Yes. UTF-8 uses 8-bit code units, so each UTF-8 code unit is one byte.

Is one UTF-16 code unit always one character?

No. Supplementary code points require two UTF-16 code units.

Does UTF-32 use one code unit per character?

It uses one code unit per code point, but a visible grapheme may contain several code points.

Why is the length of an emoji two in JavaScript?

Because JavaScript counts UTF-16 code units and many emoji use a surrogate pair.

What does Python len() count?

For normal Python strings, it generally counts code points, not bytes or grapheme clusters.

What does PHP strlen() count?

Bytes.

What should I use for a visible-character limit?

Grapheme clusters.

What should I use for a database or API size limit?

Use the unit required by the database or protocol, often bytes.

Can two identical-looking strings have different code-unit counts?

Yes. Combining characters and normalization can produce different code-point and code-unit sequences.

Are code-unit indexes portable between UTF-8 and UTF-16?

No. An index in one encoding does not directly correspond to an index in another.

References