What Is a Grapheme Cluster?

A grapheme cluster is a sequence of one or more Unicode code points that users typically perceive as a single character.

On this page

Grapheme clusters at a glance

ConceptMeaning
Grapheme clusterOne user-perceived character
Code pointOne numeric Unicode value
Code unitOne unit used by UTF-8, UTF-16 or UTF-32
ByteAn 8-bit storage unit
One grapheme equals one code point?Not always
One grapheme equals one code unit?Not always
Best useCursor movement, visible-character limits, deletion and truncation

A grapheme cluster answers β€œWhat does the user see as one character?” Code points and code units describe lower-level Unicode and encoding representations.

What exactly is a grapheme cluster?

A grapheme cluster is a sequence of Unicode code points treated as one text element. It often corresponds to what a user perceives as one character, may contain one code point or several, and is a text-segmentation concept, not an encoding. It is independent of UTF-8 or UTF-16 storage and depends on Unicode grapheme-boundary rules used for cursor movement, deletion, selection and visible-character counting.

DisplayCode pointsGrapheme clusters
AU+00411
Γ©U+00E91
éU+0065 U+03011
πŸ‘πŸ½U+1F44D U+1F3FD1
πŸ‡ΊπŸ‡ΈU+1F1FA U+1F1F81
πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦Multiple code points1

Use the Unicode Sequence Analyzer or Unicode Character Counter to inspect these layers.

Grapheme cluster vs code point

A code point is one Unicode value. A grapheme cluster may contain one or several code points, so code-point count is not always visible-character count. Some visible characters use precomposed code points; others use a base character plus combining marks or emoji sequence code points.

Γ©
Precomposed: U+00E9
Decomposed: U+0065 U+0301
RepresentationGrapheme clustersCode points
U+00E911
U+0065 U+030112

The two strings may look identical while containing different code-point sequences. Read What Is a Unicode Code Point?, Code Points vs Code Units and try the Unicode Normalizer.

Grapheme cluster vs code unit

Code units belong to a particular encoding. UTF-8 uses 8-bit code units and UTF-16 uses 16-bit code units. One grapheme may require many code units, while grapheme count does not depend on whether text is stored as UTF-8 or UTF-16.

πŸ˜€
1 grapheme cluster
1 code point
4 UTF-8 code units
2 UTF-16 code units

πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦ may be one grapheme cluster while using many code points, UTF-8 bytes and UTF-16 code units.

TextGraphemesCode pointsUTF-8 code unitsUTF-16 code units
A1111
Γ©1121
é1232
πŸ˜€1142
πŸ‘πŸ½1284

Interactive grapheme-cluster analyzer

This local component segments text into clusters, shows boundaries and lists contained code points, bytes and UTF-16 code units.

Grapheme-cluster analyzer

Segment text into user-perceived characters and inspect the code points inside each cluster.

Processed locally in your browser

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

Open Unicode Sequence Analyzer Open Unicode Character Counter

Combining characters

A combining character modifies a preceding base character. Combining marks can represent accents, vowel signs and other modifications; they usually do not form separate user-perceived characters. Multiple marks may follow one base, and rendering depends on font support and shaping.

e + β—ŒΜ β†’ Γ©
U+0065 U+0301

a + β—ŒΜŠ β†’ Γ₯

Combining sequences can be canonically equivalent to precomposed characters. Inspect with Unicode Character Inspector, Unicode Normalizer and Unicode Normalization Checker.

Emoji modifier sequences

Some emoji use a base emoji plus a modifier. Skin-tone modifiers are separate code points, and the combined sequence is usually one grapheme cluster. Removing part of the sequence changes the displayed result.

πŸ‘πŸ½
U+1F44D THUMBS UP SIGN
U+1F3FD EMOJI MODIFIER FITZPATRICK TYPE-4

1 grapheme cluster
2 code points
8 UTF-8 bytes
4 UTF-16 code units

The exact artwork depends on the platform.

Zero-width joiner emoji sequences

U+200D is ZERO WIDTH JOINER. It can join emoji into a single displayed sequence while the code points remain separate. Composite emoji can represent a family, profession or other sequence, and unsupported platforms may show separate emoji.

πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦
MAN
ZWJ
WOMAN
ZWJ
GIRL
ZWJ
BOY

Flag emoji and regional indicators

Many flag emoji use pairs of regional indicator symbols. Each regional indicator is a separate code point, and a valid pair may display as one flag grapheme cluster. Unsupported systems may show separate symbols or letters.

πŸ‡ΊπŸ‡Έ
U+1F1FA REGIONAL INDICATOR SYMBOL LETTER U
U+1F1F8 REGIONAL INDICATOR SYMBOL LETTER S

1 grapheme cluster
2 code points
8 UTF-8 bytes
4 UTF-16 code units

Variation selectors

Variation selectors request a particular presentation. U+FE0E commonly requests text presentation, while U+FE0F commonly requests emoji presentation. The base character and variation selector may form one grapheme cluster, and selectors are usually invisible by themselves.

β™₯
β™₯️

The sequences can differ even when similar. Use the Invisible Character Detector and Unicode Sequence Analyzer.

What is an extended grapheme cluster?

Unicode text segmentation defines grapheme-boundary rules. Extended grapheme clusters are the practical default used by modern software because they handle combining marks, emoji sequences, regional indicators and script-specific behavior better than legacy clusters. For most application development, β€œgrapheme cluster” should mean an extended grapheme cluster as defined by Unicode text-segmentation rules.

They are useful for interaction, but they do not perfectly match linguistic characters in every language. Boundaries can evolve as Unicode rules are updated, so applications should use maintained Unicode-aware libraries rather than manually coding a few special cases.

Grapheme boundaries and Unicode text segmentation

Unicode Standard Annex #29 defines default grapheme-cluster boundaries. Boundary rules determine where software may split text, using properties such as grapheme-break classes. Some boundaries are suppressed around combining marks, emoji modifiers, ZWJ sequences and regional indicators. Default segmentation may be tailored for specific languages or product behavior, and Unicode-version updates can change behavior.

A | Γ© | πŸ‘πŸ½ | πŸ‡ΊπŸ‡Έ | πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦

Each vertical line represents a grapheme boundary. The full algorithm belongs in maintained libraries, not ad hoc application code.

Why string length can be misleading

Standard length functions may count bytes, UTF-16 code units, code points or grapheme clusters. No single length function is correct for every requirement.

Environment or functionUsually counts
JavaScript String.lengthUTF-16 code units
JavaScript [...text].lengthCode points
JavaScript Intl.SegmenterGrapheme clusters
Python len(text)Code points
PHP strlen()Bytes
PHP mb_strlen()Encoding-aware characters/code points
Grapheme-aware PHP functionsGrapheme clusters
Database byte-length functionBytes
Database character-length functionDatabase-defined characters

For πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦, byte, code-unit, code-point and grapheme counts all differ.

Grapheme clusters in JavaScript

JavaScript strings use UTF-16 code units. String.length does not count visible characters, and spread syntax iterates code points, not grapheme clusters. Intl.Segmenter is preferred where available.

const text = "πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦";

console.log(text.length);
console.log([...text].length);

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

const graphemes = [...segmenter.segment(text)];
console.log(graphemes.length); // Usually 1
console.log(graphemes.map(item => item.segment));
function splitGraphemes(text, locale = "en") {
    const segmenter = new Intl.Segmenter(locale, {
        granularity: "grapheme",
    });

    return [...segmenter.segment(text)].map(item => item.segment);
}

Check support and use a fallback library for older environments.

Grapheme clusters in Python

Python iteration normally yields code points, and len() does not count grapheme clusters. The standard library does not provide a direct extended-grapheme iterator, so a maintained library may be needed.

import regex

text = "πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦"
graphemes = regex.findall(r"\X", text)

print(len(graphemes))
print(graphemes)
text = "e\u0301"

print(len(text))  # 2 code points
print(len(regex.findall(r"\X", text)))  # 1 grapheme cluster

\X matches an extended grapheme cluster in Unicode-aware regex implementations. Pin and test the dependency against the Unicode version you use.

Grapheme clusters in PHP

PHP strings are byte sequences. strlen() counts bytes. mb_strlen() is multibyte-aware but should not automatically be described as grapheme-aware. The Intl extension provides grapheme functions when available.

$text = "πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦";

echo strlen($text);
echo mb_strlen($text, "UTF-8");
echo grapheme_strlen($text);
$text = "AΓ©πŸ‘πŸ½";
$length = grapheme_strlen($text);

for ($index = 0; $index < $length; $index++) {
    $cluster = grapheme_substr($text, $index, 1);
    echo $cluster . PHP_EOL;
}

Use intl for grapheme functions and mbstring for multibyte operations.

Safe text truncation

Byte-safe truncation is for payload, file-size and encoded storage limits and must not cut a UTF-8 sequence. Code-point-safe truncation applies only when the requirement limits Unicode scalar values and may still split a visible grapheme. Grapheme-safe truncation is usually right for usernames, display names, chat previews, buttons, labels, editor cursor behavior and user-facing character limits.

RequirementCorrect unit
Maximum UTF-8 payloadBytes
Maximum Unicode valuesCode points
Maximum visible charactersGrapheme clusters
JavaScript API offsetUsually UTF-16 code units
Database field sizeDepends on database definition
UI text truncationGrapheme clusters

Safe deletion and cursor movement

Backspace should normally remove one grapheme cluster. Cursor movement should avoid stopping between a base character and combining mark, and editors should avoid placing the caret inside a ZWJ emoji sequence. Selection boundaries should respect grapheme segmentation when practical. Low-level APIs may expose code-unit indexes even when the UI acts on grapheme clusters. Bidirectional text and shaping add complexity beyond grapheme segmentation.

Character limits in forms and usernames

Product requirements must specify the unit. Database capacity and API limits may be byte-based, security checks may inspect code points, and user-facing limits should usually count grapheme clusters. Normalization can alter code-point and byte counts, while confusable and invisible characters require separate validation. A grapheme limit alone does not prevent abuse with very long combining sequences.

  1. Validate encoded byte limit.
  2. Validate Unicode scalar values.
  3. Normalize only when required.
  4. Count grapheme clusters for user-facing limits.
  5. Limit pathological combining sequences.
  6. Detect invisible or disallowed controls.
  7. Apply product-specific script or identifier rules.

Use Invisible Character Detector, Confusable Character Detector, Unicode Normalizer and Unicode Character Counter.

Common grapheme-cluster mistakes

Treating JavaScript length as visible characters

It counts UTF-16 code units.

Treating spread syntax as grapheme-aware

Spread syntax iterates code points.

Treating Python length as grapheme count

It normally counts code points.

Treating mb_strlen() as sufficient for every UI limit

Multibyte-aware length and grapheme-aware length solve different problems.

Splitting text by arbitrary code-point indexes

Combining sequences and emoji can break.

Truncating UTF-8 by bytes without validation

This can create broken sequences.

Deleting only part of an emoji sequence

You can leave orphaned modifiers or joiners.

Assuming normalization produces one code point per grapheme

Normalized text can still contain multi-code-point clusters.

Assuming one grapheme has a fixed visual width

Emoji, CJK characters, combining marks and fonts vary.

Implementing segmentation with a simple regular expression

Unicode grapheme-boundary rules are more complex.

Ignoring Unicode-version changes

Segmentation data and rules evolve.

Practical debugging workflow

  1. Preserve the original text.
  2. Count encoded bytes.
  3. Inspect code points.
  4. Inspect UTF-16 code units where relevant.
  5. Segment grapheme clusters.
  6. Check normalization.
  7. Detect combining marks.
  8. Detect zero-width joiners and variation selectors.
  9. Test cursor movement and deletion.
  10. Test with accented text, emoji, flags and non-Latin scripts.

Start with Unicode Sequence Analyzer, Unicode Character Counter, Unicode Character Inspector, Unicode Normalizer, Invisible Character Detector and Unicode Text Compare.

Try these UnicodeNow tools

These tools help inspect graphemes, code points, byte length, normalization and hidden characters in real input.

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

Unicode Character Inspector

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

UnicodeProcessed 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 a grapheme cluster?

A sequence of one or more Unicode code points that users generally perceive as one character.

Is a grapheme cluster the same as a character?

It is usually the closest technical unit to a user-perceived character, though linguistic and rendering behavior can be more complex.

Can one grapheme cluster contain multiple code points?

Yes. Combining characters, emoji modifiers, flags and ZWJ sequences commonly use multiple code points.

Is an emoji always one grapheme cluster?

Many displayed emoji are one grapheme cluster, but their internal code-point sequences vary, and unsupported rendering may display them differently.

Why is Γ© sometimes one code point and sometimes two?

It may use precomposed U+00E9 or decomposed U+0065 followed by U+0301.

Why does JavaScript report a large length for one emoji?

Because String.length counts UTF-16 code units rather than grapheme clusters.

Does Python len() count grapheme clusters?

No. It normally counts code points.

Does PHP have grapheme-aware functions?

Yes, when the Intl extension is available, functions such as grapheme_strlen() and grapheme_substr() can be used.

Should username limits count grapheme clusters?

Usually for user-facing length, but byte limits, security validation and pathological combining sequences must also be handled.

Can normalization change grapheme-cluster count?

It can change code-point sequences and may affect segmentation in some cases, so behavior should be tested.

Can one grapheme cluster contain many bytes?

Yes. Complex emoji and combining sequences can require many UTF-8 bytes.

Is grapheme segmentation the same in every Unicode version?

Rules and character data can evolve, so applications should use maintained Unicode-aware implementations.

References