In Unicode, "character" has several meanings, and each one gives a different count. A code point is the number assigned to a character. A code unit is the fixed-size piece that stores code points in memory or in a file. A grapheme cluster is what a reader sees as one character, and it can contain several code points. A count meant for people should use grapheme clusters; a storage limit is set in code units or bytes.

Four units, side by side

Unit What it measures é as one code point é as e + accent 🙂 👨‍👩‍👧‍👦
Grapheme cluster Characters as a reader sees them 1 1 1 1
Code point Entries in the Unicode code space 1 2 1 7
UTF-16 code unit 16-bit storage units 1 2 2 11
UTF-8 code unit (byte) 8-bit storage units 2 3 4 25

The Unicode Consortium's own FAQ lists four approaches to measuring a string (bytes, code units, code points and grapheme clusters) and says the right one "depends on what is being counted and what the count or position is used for".

Abstract characters and code points

An abstract character is the idea of a character, separate from any font or file format. The Unicode Glossary defines it as a "unit of information used for the organization, control, or representation of textual data". The letter é is an abstract character whether it is printed, handwritten or stored.

A code point is a number in the Unicode code space, which the glossary gives as the integers from 0 to 10FFFF in hexadecimal. That is 1,114,112 possible values. Code points are written with a U+ prefix, so the letter A is U+0041 and 🙂 is U+1F642. Not every code point is assigned to a character.

Two facts about code points explain many counting surprises:

  • One abstract character can have more than one spelling. The letter é can be the single code point U+00E9, or the letter e (U+0065) followed by U+0301 COMBINING ACUTE ACCENT.
  • Some code points are not characters a reader would ever count. U+200D ZERO WIDTH JOINER and U+FE0F VARIATION SELECTOR-16 are invisible instructions that sit inside many emoji.

So a code point count is exact and stable, but it does not match what people see.

Code units: how code points are stored

A code point is an abstract number. To store it, an encoding form turns it into one or more code units of a fixed size. The glossary calls a code unit the "minimal bit combination that can represent a unit of encoded text for processing or interchange", and Unicode has three sizes: 8-bit units in UTF-8, 16-bit units in UTF-16 and 32-bit units in UTF-32.

Character Code point UTF-8 code units UTF-16 code units
A U+0041 41 0041
é U+00E9 C3 A9 00E9
€ U+20AC E2 82 AC 20AC
🙂 U+1F642 F0 9F 99 82 D83D DE42

UTF-16 fits every code point up to U+FFFF in one unit. Higher code points take two units, called a surrogate pair: a high surrogate in the range D800 to DBFF followed by a low surrogate in the range DC00 to DFFF. UTF-8 uses between one and four bytes.

Code units matter because many programming languages report string length in them. The Unicode FAQ notes that the code unit count "often represents the 'length' of the string in APIs". JavaScript is a familiar case, covered in the guide to JavaScript string length. Byte counts are covered in UTF-8 bytes vs characters.

Grapheme clusters: what a reader sees

Unicode Standard Annex #29 uses the term "user-perceived character" for the thing people mean when they say character, and defines the grapheme cluster as the way to find one in software. The annex calls grapheme clusters "a best-effort approximation that can be determined programmatically and unambiguously", without needing to know the language or the font.

The default kind is the extended grapheme cluster. An older, narrower definition called the legacy grapheme cluster still exists for compatibility, and the annex recommends the extended kind.

On counting, the annex is direct. Where software has to give users a character count, "the counts should correspond to the number of segments delimited by grapheme cluster boundaries". The main Characters figure in the Character Counter follows that advice: it counts extended grapheme clusters using the segmenter built into your browser.

Combining marks and normalization

The word café can be stored in two ways that look identical on screen.

Spelling of café Visible characters Code points UTF-16 units UTF-8 bytes
c, a, f, é (U+00E9) 4 4 4 5
c, a, f, e, U+0301 4 5 5 6

Unicode calls these two sequences canonically equivalent: they stand for the same text. Software that compares code points one by one still treats them as different strings with different lengths. That affects equality checks, duplicate detection, search and any length validation that counts code points or code units.

Normalization converts text to one agreed spelling. Unicode Standard Annex #15 defines the two forms that matter here:

  • NFD is canonical decomposition. Precomposed letters are split into a base letter plus combining marks. Both spellings of café become 5 code points.
  • NFC is canonical decomposition followed by canonical composition. Marks are folded back into precomposed letters where one exists. Both spellings become 4 code points.

Normalizing both strings to the same form before comparing them removes the mismatch. You cannot tell which form a piece of text is in by looking at it.

Normalization does not turn code points into visible characters. Many combinations have no precomposed form. The letter g with a diaeresis, g̈, is g followed by U+0308 in every normalization form: 2 code points, 1 grapheme cluster. Only grapheme cluster counting gives 4 for both spellings of café and 1 for g̈ without any preparation.

What an extended grapheme cluster includes

The rules in Annex #29 keep the following together as one cluster:

  • A base character and its combining marks. The letter e followed by U+0301 is one cluster.
  • A carriage return followed by a line feed. The Windows line break CRLF is two code points and one cluster. Line one, a CRLF and Line two come to 18 code points but 17 clusters.
  • Hangul syllables. The Korean syllable 각 can be the single code point U+AC01 or three conjoining letters, U+1100, U+1161 and U+11A8. Either way it is one cluster.
  • Syllables in Indic scripts. The annex lists Devanagari क्षि as one extended grapheme cluster. It is four code points: U+0915, U+094D, U+0937 and U+093F.
  • Emoji sequences. Skin tone modifiers, sequences joined with U+200D and pairs of regional indicators that form a flag each make a single cluster. The guide to emoji character counts breaks these down.

Three limits of the definition are useful to know.

First, a cluster is not always what a particular reader calls a letter. The annex gives the Slovak digraph ch as something a language-specific version of the rules might treat as one cluster. Under the default rules it is two.

Second, every code point belongs to some cluster. A control character, or an invisible format character such as U+200B ZERO WIDTH SPACE, forms a cluster by itself and adds 1 to the count even though nothing appears on screen.

Third, a cluster has no maximum size. A base letter can carry any number of combining marks and still be one cluster. The annex makes the point that a grapheme cluster limit is "a user-perceived limit, just like a word count; it is not a data size limit", and suggests pairing it with a byte limit when storage has to be bounded.

The rules change between Unicode versions

Annex #29 is revised as the Unicode Standard is updated. The current version of the standard is Unicode 18.0, announced in September 2026, and the annex was reissued for it.

The rules themselves move. The 18.0 edition lists a change to rule GB9c, which keeps certain Indic consonant clusters together, and that rule is a recent addition: the Unicode 15.0 edition of the annex does not contain it. Text in a script such as Devanagari can therefore be divided differently by two programs built against different versions. The annex also allows implementations to adapt the rules for particular languages.

For a counter that relies on the browser, as this site's does, the result comes from the Unicode data inside that browser. Two browsers, or two versions of one browser, can disagree slightly on text that uses the affected sequences.

Code point, UTF-16 and UTF-8 counts do not have this problem. Each encoding is a fixed, reversible mapping from code points, so those numbers are the same in every correct implementation.

Which unit to use

  • A limit that people read and write to, such as an essay or a profile field: grapheme clusters, unless the form says otherwise.
  • JavaScript string positions, or the HTML maxlength attribute: UTF-16 code units.
  • File size, network payloads and byte-limited fields: UTF-8 bytes.
  • A database column: whichever unit that database uses for the column type. The guide to database field limits lists the main ones.
  • Comparing or deduplicating text: normalize both sides to the same form first, then compare.

When you are not sure which unit a system uses, paste the text into the Character Counter and open Advanced statistics. It shows code points, UTF-16 code units and UTF-8 bytes next to the visible character count, and the methodology page describes how each is calculated.