Letras Diferentes
Unicode research · Character model

Unicode Code Points, Characters, Grapheme Clusters, and Glyphs: A Technical Model

Distinguish abstract characters, Unicode code points, code units, grapheme clusters, and rendered glyphs with reproducible Unicode and JavaScript examples.

Technical referenceUnicode baseline: 18.0.0

Five different layers of digital text

When an application says “one character,” it may be counting bytes, UTF-16 code units, Unicode code points, grapheme clusters, or visible glyphs. Those are not interchangeable units. Unicode supplies a character repertoire and a standard encoding model; a text renderer then maps stored content to visible shapes. Reliable text software explicitly names the unit it operates on.

ConceptWhat it representsPractical consequence
Abstract characterA unit of textual information, such as LATIN CAPITAL LETTER A, independent of its font.Identity is not its appearance or keyboard key.
Code pointAn integer in the Unicode codespace, written U+0000 through U+10FFFF.A code point may be assigned to a character, reserved, a surrogate, private-use or a noncharacter.
Code unitA storage unit in an encoding form: 8 bits for UTF-8, 16 bits for UTF-16, or 32 bits for UTF-32.A code point can require multiple code units.
Extended grapheme clusterA default Unicode approximation of a user-perceived character, determined by UAX #29 boundary rules.One cluster can contain several code points; preferred for many UI selection/counting tasks.
GlyphA shape or glyph ID chosen by a font and shaping process for rendering.One glyph can correspond to several characters, or one character to multiple glyphs.

It is important not to flatten the distinctions further. A code point is a numeric position in a 1,114,112-position codespace, not necessarily a valid scalar value or an assigned abstract character. Likewise, a glyph is not a Unicode property: its identity depends on the selected font, script shaping rules, variation settings and context. Unicode does not prescribe a particular visual drawing for each code point.

Terminology rule: When documenting APIs or database constraints, say “UTF-8 bytes,” “UTF-16 code units,” “Unicode scalar values,” or “extended grapheme clusters.” Avoid the ambiguous word “characters” where a numeric limit matters.

Worked examples: identical appearance, different data

A letter with an accent: two ways to encode é

The displayed letter é can be represented by the single precomposed code point U+00E9 (LATIN SMALL LETTER E WITH ACUTE) or by U+0065 U+0301 (LATIN SMALL LETTER E followed by COMBINING ACUTE ACCENT). The latter is two code points but typically one extended grapheme cluster. They are canonically equivalent under Unicode normalization, yet their raw byte sequences differ. Exact byte matching without normalization can therefore reject text a reader perceives as identical.

const precomposed = "\u00E9";       // U+00E9
const decomposed = "e\u0301";      // U+0065 U+0301
console.log(precomposed === decomposed);      // false
console.log(precomposed.normalize("NFD")
         === decomposed.normalize("NFD"));    // true
console.log([...decomposed].length);          // 2 code points

Canonical equivalence is stronger than mere visual resemblance. The Latin A and Cyrillic А may look alike in some fonts but are not canonically equivalent. Conversely, a font can render two canonically equivalent strings differently due to imperfect shaping or glyph coverage.

A ligature: one glyph, multiple characters

With a suitable OpenType font and ligature feature, the two code points U+0066 U+0069 (“fi”) may be drawn as a single ligature glyph. The underlying text remains two code points. This is different from the compatibility character U+FB01 LATIN SMALL LIGATURE FI, whose NFKC mapping is “fi”. Glyph-counting is therefore not a dependable way to measure string length, selection boundaries, or storage.

An emoji sequence: multiple code points, one cluster

The woman technologist sequence U+1F469 U+200D U+1F4BB joins WOMAN, ZERO WIDTH JOINER, and PERSONAL COMPUTER. In emoji-aware presentation it is usually displayed as one composite symbol. It contains three code points and five UTF-16 code units but is one extended grapheme cluster under the recommended segmentation algorithm. If a platform lacks sequence support, its fallback may display two pictographs and/or a visible separator without changing the stored code points.

Flag sequences provide another example: a common national flag is encoded using two regional indicator code points, each supplementary, typically forming a single extended grapheme cluster. Glyph composition is not a reliable proxy for stored length.

Counting, slicing, searching, and cursor behavior

Choose the unit according to the operation, rather than choosing a single “Unicode-aware string length” for everything:

  • Storage quotas and transport: Count bytes in the actual encoding (for example, UTF-8). A character count does not bound the number of bytes.
  • JavaScript string offsets: Built-in length, indexing, and slice use UTF-16 code units. Offsets may split a surrogate pair or a grapheme sequence.
  • Code-point iteration: for…of, Array.from(), and codePointAt() support code-point-aware work, but do not by themselves segment grapheme clusters.
  • User-facing cursor movement or length limits: Extended grapheme clusters, plus language- and editor-specific behavior, are often more appropriate. Some script-specific editing conventions require tailored boundaries.
  • Searching and equality: Raw code-point equality, canonical equivalence, compatibility equivalence and locale-aware collation answer different questions. Explicitly select the required semantics.

The default extended grapheme cluster algorithm is maintained in Unicode Standard Annex #29. It is a deterministic segmentation boundary algorithm, not a universal statement that users always perceive exactly the same number of “characters.” Different editing systems can tailor behavior, and the output of a character counter can vary with the Unicode data version available in its runtime.

Why fonts and shaping engines change the visible result

A typical text pipeline is: decode bytes into code points; segment and reorder where required by layout; choose fonts with suitable glyph coverage; shape character runs into positioned glyphs; and rasterize those glyphs. In complex scripts, substitutions and positioning depend on nearby characters. Combining marks may be placed above, below or around a base; Arabic joining and Indic syllable shaping can replace simple one-to-one glyph mappings.

Font fallback can split a sequence across fonts that do not share suitable mark anchors or glyph metrics. This is one reason copyable styled Unicode text may appear differently in mobile apps, browsers, spreadsheets and messaging clients even though the underlying code points are unchanged. A practical comparison is to generate styled Unicode characters in the Fancy Text tool and inspect their actual code points and fallback glyphs on different devices. A missing-glyph box (“tofu”) indicates rendering support is missing, not necessarily corrupt or invalid Unicode data.

Glyph names, font character maps (cmap), glyph IDs, and Unicode code-point values occupy different namespaces. Searching for a glyph ID in the Unicode Character Database is a category mistake: a font designer can encode multiple alternatives for the same character, and a ligature glyph can cover a sequence of characters.

Implementation example: three kinds of string length

The following example measures UTF-8 bytes, UTF-16 code units, Unicode code points, and extended grapheme clusters in JavaScript. It uses standard browser/modern Node interfaces; Intl.Segmenter availability and segmentation data are runtime-dependent.

function measureText(text) {
  const graphemes = new Intl.Segmenter("en", {
    granularity: "grapheme"
  });
  return {
    utf8Bytes: new TextEncoder().encode(text).length,
    utf16Units: text.length,
    codePoints: [...text].length,
    graphemes: [...graphemes.segment(text)].length
  };
}
console.log(measureText("e\u0301"));
// { utf8Bytes: 3, utf16Units: 2,
//   codePoints: 2, graphemes: 1 }

console.log(measureText("\u{1F469}\u200D\u{1F4BB}"));
// { utf8Bytes: 11, utf16Units: 5,
//   codePoints: 3, graphemes: 1 }

Do not use the simple count as a byte limit for an upload, or treat the grapheme count as a cap on allocation: UAX #29 explicitly notes that an extended grapheme cluster has no fixed size bound. For security-sensitive applications, cap both encoded bytes and application-appropriate grapheme counts.

Edge cases and common engineering errors

  • Combining marks alone: A leading combining mark may form a degenerate grapheme cluster rather than attach to a preceding base character that does not exist. A renderer may show a dotted-circle placeholder.
  • Variation selectors: These modify presentation in supported contexts and are code points that may not produce independent visible glyphs.
  • Invisible formatting characters: Some controls affect join behavior or direction without contributing a visible shape. “No visible glyph” does not mean “safe to strip.”
  • Ill-formed UTF-16: An unpaired surrogate is not a Unicode scalar value. JavaScript string storage can contain it, but a strict UTF encoder cannot reproduce it as a legal Unicode scalar value.
  • Version dependence: Unicode updates can add assignments and adjust segmentation rules, so fixed runtime behavior should not be assumed across browser and server versions.
  • Security: Spoofing defenses require confusable, script and identifier policies. Neither glyph similarity nor normalization alone can prove two identifiers are the same.

Reproducible test vectors

TextUTF-8 bytesUTF-16 unitsCode pointsExtended grapheme clusters
A1111
e + U+03013221
U+1F600 (grinning face)4211
U+1F469 U+200D U+1F4BB11531
U+1F1FA U+1F1F8 (US flag sequence)8421

The grapheme-cluster counts use the recommended extended rules of UAX #29 (Unicode 18.0). They are independent of whether the current font displays an integrated pictograph.

Standards and primary references

  1. The Unicode Standard, Version 18.0.0, chapters on character encoding, code points and character properties.
  2. UAX #29 — Unicode Text Segmentation, default extended grapheme clusters and boundary rules.
  3. UAX #44 — Unicode Character Database, formal properties of code points.
  4. UAX #15 — Unicode Normalization Forms, canonical equivalence and normalization.
  5. MDN — Intl.Segmenter, the JavaScript segmentation interface.

This reference explains public Unicode specifications independently; it is not a publication or endorsement of the Unicode Consortium. Rendering and runtime behavior should be verified against the software versions in use.