A character counter reports how many characters are in a string. The interesting question is what counts as one character. The tool above runs in your browser and reports several counts (characters with and without whitespace, words, lines, bytes, code points); this page explains why the counts can differ from each other and from what users expect.
Four definitions of “character”
| Definition | What it counts | Where it’s used |
|---|---|---|
| Bytes | Raw bytes of the encoded string | Database varchar limits in some systems, network protocol limits |
| Code units | UTF-16 16-bit units (JavaScript string.length) | JavaScript-native APIs |
| Code points | Unicode characters (after combining surrogate pairs) | Most APIs that say “character” |
| Grapheme clusters | What a human perceives as one character | User-facing counts (tweet limits, form labels) |
For ASCII-only input, all four agree. For everything else, they can diverge.
Where they disagree
String: "café"
Bytes (UTF-8): 5 (c=1, a=1, f=1, é=2)
UTF-16 units: 4 (each char fits in one unit)
Code points: 4
Grapheme clusters: 4
String: "👨👩👧" (family emoji = man + ZWJ + woman + ZWJ + girl)
Bytes (UTF-8): 18
UTF-16 units: 8 (three surrogate pairs + two ZWJ)
Code points: 5 (M, ZWJ, W, ZWJ, G)
Grapheme clusters: 1 ← what users see
String: "👋🏽" (waving hand with skin tone modifier)
Bytes (UTF-8): 8
UTF-16 units: 4
Code points: 2 (hand + skin tone)
Grapheme clusters: 1 ← what users see
When a user pastes a “single emoji” and the counter says 7, the counter is reporting code points, not grapheme clusters. The emoji is one perceived character but multiple code points.
What the tool above reports
- Characters. Grapheme clusters — what a reader sees as one character — counted with the browser’s
Intl.Segmenter. A thumbs-up with a skin tone is 1. - Characters (no spaces). The same, minus whitespace.
- Words. Whitespace-delimited tokens.
- Lines. Line-break-delimited segments (see below).
- Bytes (UTF-8). Useful for database column sizing.
- Code points and UTF-16 units — the numbers most programming languages report (
len()in Python 3 counts code points;.lengthin JavaScript counts UTF-16 units). - Unique code points, spaces, letters, digits and the five most frequent characters.
Why the differences matter
- Twitter / X tweet limits. Historically counted code points (with some characters weighted as 2). Updates have moved closer to grapheme clusters. The visible-character count drives the limit.
- SMS message limits. GSM-7 encoding fits 160 7-bit characters; UCS-2 fits 70 16-bit code units; longer messages are split into segments. A tweet with one emoji silently switches encoding and halves the per-segment limit.
- Database columns. A
VARCHAR(50)in MySQL means 50 characters inutf8mb4charset;BYTEAand some other types are byte-counted. A 50-byte limit fits 50 ASCII or only 12 of the 4-byte family-emoji clusters. - Form validation. Telling a user “max 280 characters” and rejecting their post because your code count differs from theirs is a usability bug. Use grapheme clusters for limits the user perceives.
Words and how they’re counted
Words are even messier than characters:
- Whitespace tokenization. The simplest definition: split on whitespace. Works for English; fails for Chinese, Japanese, Thai (no spaces between words).
- Word-boundary detection. Unicode defines word-boundary rules (UAX #29). Modern segmenters use these.
- Hyphenation. Is “well-known” one word or two? The convention varies; whitespace splitters usually call it one.
- Apostrophes. “Don’t” is one word. “Rock ‘n’ roll” is — depending on the splitter — one to four.
The tool above uses whitespace tokenization, which matches the conventional word-counter UX (Microsoft Word, Google Docs). For Unicode-aware segmentation, browser Intl.Segmenter is the right primitive.
Lines
A “line” is everything up to a line terminator. The tool above counts content lines: one\ntwo is two lines, and so is one\ntwo\n — a final newline ends the last line rather than starting an empty one.
Edge cases:
- CRLF vs LF. Both terminate a line; the count is the same. The tool treats CRLF as a single line break.
- Trailing newline. A file that ends with
\nhas the same line count as one without. That differs fromwc -l, which counts newline characters:wc -lsays 1 forone\ntwo. - Blank lines in the middle. They count —
one\n\ntwois three lines.
Common pitfalls
- Using
string.lengthin JavaScript as a character count.string.lengthreturns UTF-16 code units. Surrogate-pair characters (most emoji) count as 2. Astral-plane Unicode (some Chinese, math symbols) counts as 2. For human-perceived counts, useIntl.Segmenter. - Mismatched counters between client and server. A browser counting grapheme clusters and a Python server counting bytes will disagree on long inputs. Decide which definition is canonical, document it, and align both ends.
- Counting whitespace as content. “1,500 characters with spaces” looks longer than “1,500 characters without spaces” — same content, different number. Most word-count UIs report both.
For other text utilities, see the word counter, line counter, and text case converter.