String Length, Four Ways
After reading this you will know why one string can have four different "lengths" at once, and which count your database, your programming language and your users each care about.
Four counts for one string
Type the family emoji 👨👩👧 into a text box and ask for its length. JavaScript says 8. Python 3 says 5. Your eyes say 1. A UTF-8 byte counter says 18. Nobody is lying. Each number answers a different question.
Text is stored in layers. Users see graphemes, the visible units you can select with one caret move. Unicode describes text as a sequence of code points, integers from 0 to 1114111. Machines store those code points as bytes, using an encoding such as UTF-8 or UTF-16. The String Length Inspector shows all four at once so you can see where they diverge.
- Grapheme
- A user-perceived character, one visible glyph, defined by Unicode's grapheme cluster rules (UAX #29).
- Code point
- One Unicode scalar value in the range
U+0000toU+10FFFF. - UTF-8 byte
- One byte of the UTF-8 encoding. Each code point takes 1 to 4 bytes.
- UTF-16 code unit
- One 16-bit unit. Code points above
U+FFFFtake 2 units (a surrogate pair).
When these counts matter
You reach for byte counts when something has a byte budget. A database column declared VARCHAR(20) may store 20 bytes or 20 characters depending on the engine and charset. A DNS label is capped at 63 bytes. An HTTP header value, a QR code, a fixed-width protocol field: all measured in bytes.
You care about UTF-16 units when you validate input in JavaScript, Java, or C#, because their string .length counts UTF-16 units. You care about graphemes when a human sees the result: a 280-character tweet limit, a username display, a truncation preview.
Truncating a string by cutting at byte offset 10 can split a multi-byte code point in half and produce invalid UTF-8. Truncating by UTF-16 units can split a surrogate pair. Truncating by code points can break a grapheme cluster, chopping an emoji's skin-tone modifier off its base. Only grapheme-aware truncation is safe for display.
How UTF-8 spends bytes
UTF-8 (defined in RFC 3629) is a variable-width encoding. The rule is simple once you see it as ranges:
Here c is the code point value. ASCII (U+0000 to U+007F) fits in one byte, which is why English text costs one byte per character. Latin letters with accents and most European scripts land in the 2-byte range. The bulk of the Basic Multilingual Plane, including é's neighbours, CJK ideographs and most symbols, takes 3 bytes. Emoji and rare scripts above U+FFFF take 4.
UTF-16 splits differently. Everything up to U+FFFF is one 16-bit unit. Everything from U+10000 up is a surrogate pair, so two units. There is no 3-unit case.
Worked example: the family emoji
Breaking down 👨👩👧
The sequence looks like one face-and-family glyph. Under the hood it is three human emoji joined by an invisible connector.
| Code point | Name | UTF-8 bytes | UTF-16 units |
|---|---|---|---|
| U+1F468 | Man | 4 | 2 |
| U+200D | Zero Width Joiner | 3 | 1 |
| U+1F469 | Woman | 4 | 2 |
| U+200D | Zero Width Joiner | 3 | 1 |
| U+1F467 | Girl | 4 | 2 |
- Count code points: 5 entries in the table, so 5 code points.
- Sum UTF-8 bytes:
4 + 3 + 4 + 3 + 4 = 18, so 18 bytes. - Sum UTF-16 units:
2 + 1 + 2 + 1 + 2 = 8, so JavaScript's.lengthreports 8. - Apply grapheme clustering: the two zero-width joiners (
U+200D) bind the three faces into one cluster, so 1 grapheme.
That is the full spread: 1 grapheme, 5 code points, 8 UTF-16 units, 18 UTF-8 bytes. Paste the emoji into the tool and you will see these exact numbers, plus the per-character table above.
The Zero Width Joiner is the glue. Remove both joiners and you get three separate emoji: 3 graphemes, 3 code points, 12 UTF-8 bytes. The visible result changes from one family to three lone faces.
Explore the counts yourself
Reading the per-character breakdown
The tool lists each code point with its hex value, name and byte cost. Three patterns are worth recognising.
First, combining marks. The letter é can arrive two ways. As one precomposed code point U+00E9 it is 2 UTF-8 bytes and 1 grapheme. As e (U+0065) plus a combining acute accent (U+0301) it is 3 UTF-8 bytes, 2 code points, still 1 grapheme. Same glyph on screen, different counts. This is why grapheme count and code point count can disagree even without emoji.
Second, surrogate pairs. Any code point at or above U+10000 shows as 2 UTF-16 units. That is the entire reason JavaScript reports 8 for the family emoji instead of 5.
Third, invisible code points. Zero Width Joiners, variation selectors (U+FE0F forces emoji presentation) and directional marks all cost bytes but draw nothing on their own. If a byte count looks larger than expected, scan the table for these.
Common mistakes
Sizing a column to VARCHAR(255) and assuming 255 user characters. In MySQL with the utf8mb4 charset a character can take 4 bytes, and older definitions counted bytes, so a string of 255 emoji could need 1020 bytes and get rejected or silently truncated.
Validating a password or bio length with JavaScript's str.length and calling it "characters". A user who types 140 emoji sees a count of 280 and a rejected form. Count graphemes for a human-facing limit, or code points if you must count in code.
Reversing a string by reversing its code units. Reverse 👍 byte-pair by pair and you swap the surrogate halves, producing an invalid pair that renders as ?. Reverse by grapheme instead.
When you need bytes for a storage or transfer budget, convert the byte total with the Byte size converter to check it against a limit expressed in KiB or MB.
Related tools
Once you know a string's byte layout, the next questions are usually about transforming or protecting it. To turn text into a transport-safe form and back, use the Base64 / URL / HTML encoder-decoder; note that Base64 inflates byte length by about 33 percent. To fingerprint a string, the Hash generator gives you MD5 through SHA-512, and the HMAC calculator signs a message with a secret key. If your string is a token, the JWT decoder splits and reads its parts locally.
Frequently asked questions
Why does JavaScript say my emoji is length 2?
JavaScript strings are UTF-16, and any code point above U+FFFF is stored as a surrogate pair of two 16-bit units. The .length property counts units, so one astral emoji reads as 2. Use [...str].length to count code points, or an Intl.Segmenter to count graphemes.
How do I count what the user sees?
Count graphemes. That is the grapheme cluster count from Unicode UAX #29, which merges combining marks, surrogate pairs and joiner sequences into single visible units. The tool reports this as the grapheme total.
How many bytes will my text take in a database?
For a utf8mb4 or UTF-8 column, it is the UTF-8 byte count the tool shows. Multiply by the row count for a rough table estimate, and remember that a VARCHAR(n) defined in bytes will reject strings whose byte count exceeds n.
Are code points and characters the same thing?
Not always. A "character" in the everyday sense is a grapheme. One grapheme can be several code points, as with e plus a combining accent (2 code points, 1 grapheme) or the family emoji (5 code points, 1 grapheme).
Does this tool send my text anywhere?
No. All counting runs in your browser, so nothing you paste leaves your device.