UTF-8 Byte Counter | Spot the VARCHAR-vs-String.length gap on one screen
Paste text and see UTF-8 byte count, Unicode code points, UTF-16 code units (the same number JavaScript's string.length returns), and line count side by side. A 4-bucket byte breakdown (ASCII, Latin Extended, BMP CJK, emoji & supplementary) shows exactly which characters are blowing up your storage budget.
💡 About this tool
If you have ever had a 200-character string rejected by VARCHAR(255) with ERROR 1406: Data too long, watched a JWT payload triple in size after switching to an emoji-friendly username, or seen '😀'.length === 2 in the console, you have hit the gap between Unicode code points, UTF-8 bytes, and UTF-16 code units.
UTF-8 is a variable-length encoding (RFC 3629): ASCII fits in 1 byte, Latin Extended / Greek / Cyrillic in 2, the BMP CJK block in 3, and everything above U+FFFF (emoji, supplementary CJK, mathematical alphanumerics) in 4. JavaScript strings, on the other hand, are stored as UTF-16, so anything above the BMP shows up as a surrogate pair and counts as 2 in str.length. Databases enforce their limits in bytes; JS APIs reason in UTF-16 units; humans count graphemes. All three disagree the moment an emoji enters the picture.
This counter shows the four metrics side by side and then breaks every character into one of four UTF-8 byte buckets so you can answer practical questions: "Why is this Japanese tweet 3x heavier than the English equivalent?", "How many emoji can I fit in a 4 KB Redis key?", "Will this NVARCHAR(50) column hold my display name?".
🧐 Frequently Asked Questions
Q. Does VARCHAR(255) mean 255 characters or 255 bytes?
A. In MySQL 5.0.3+ it means 255 characters, but row size is enforced in bytes (65,535 max per row). With utf8mb4, a VARCHAR(N) is treated as up to N × 4 bytes for index and row-size calculations, so a single VARCHAR(16383) column already fills the row. Use the "UTF-8 bytes" reading here to size columns realistically.
Q. Why does '😀'.length return 2 in JavaScript?
A. String.prototype.length returns the count of UTF-16 code units, and U+1F600 lives outside the BMP, so it is encoded as the surrogate pair 😀. To count actual characters use [...str].length, Array.from(str).length, or this tool's "Characters" field, which iterates code points with for..of.
Q. What is the difference between MySQL utf8 and utf8mb4?
A. The legacy utf8 charset stores at most 3 bytes per character, silently corrupting any 4-byte sequence (emoji, the surname 𠮷, several mathematical symbols) into ? or 0x3F. utf8mb4 is the RFC 3629-compliant 4-byte form. Always pick utf8mb4 for new schemas; if you see anything in this tool's "4-byte" bucket, the legacy charset would have eaten it.
Q. Characters vs UTF-16 units — when does the gap matter?
A. Anywhere an API trusts string.length: React key props, naive substring truncation, Twitter character limits before 2017, password length validators. Grapheme clusters (👨👩👧👦 = one perceived character) differ from code points too — use Intl.Segmenter if you need user-facing counts.
Q. How are line endings counted?
A. LF (\n), CRLF (\r\n), and CR (\r) are all treated as a single line separator. An empty input yields 0 lines; any non-empty input yields at least 1. Note that CRLF still costs 2 bytes and 2 UTF-16 units even though it counts as one separator.
📚 Fun Facts
- Why 280 characters? Twitter raised the limit from 140 to 280 in 2017 for languages whose information density per character is lower. Japanese, Chinese, and Korean tweets stayed at 140 because each CJK character carries roughly 2x the information of a Latin character. Run a 140-char Japanese string and a 280-char English string through this counter — both land near 420–500 UTF-8 bytes, which is the actual transport budget Twitter has historically optimized for.
- The 𠮷 problem in Japan: The surname
𠮷野家(Yoshinoya beef bowl chain) uses U+20BB7, a supplementary-plane CJK ideograph that takes 4 UTF-8 bytes and 2 UTF-16 units. Legacyutf8-only databases turn it into?, which is exactly why every Japanese system built after ~2014 defaults toutf8mb4. Paste a name with𠮷and you will see one entry in the "4-byte" bucket immediately. - ZWJ emoji and grapheme inflation: The family emoji 👨👩👧👦 looks like one character but is built from four person emoji joined by U+200D Zero Width Joiners — 7 code points, 11 UTF-16 units, and 25 UTF-8 bytes. This is why naive truncation of user-generated emoji content can chop a single grapheme cluster in half and produce visual gibberish.