Skip to content
FindTool

    Character Counter

    Count characters, bytes and graphemes against platform limits like Twitter and SMS.

    Character Counter tool

    0
    Graphemes (what a reader sees)
    0
    Code points
    0
    UTF-16 units (JS length)
    0
    UTF-8 bytes
    0
    Without spaces
    0
    Whitespace characters
    0
    Lines
    0
    Longest line

    SMS segments

    —
    Encoding
    0
    Segments billed
    160
    Left in this segment

    Plain ASCII sends as GSM-7: 160 characters per message, 153 per part once it is split.

    Remaining budget

    • X / Twitter post 280 left

      CJK characters count double; links are always counted as 23.

    • Meta description 160 left

      Google truncates by pixel width near 920px, so 160 is a guideline.

    • Page title (Google) 60 left

      Also pixel-based: roughly 580px on desktop results.

    • Open Graph title 88 left

      Beyond this most social previews clip mid-word.

    • Instagram caption 2,200 left

      Only the first ~125 show before the "more" link.

    • varchar(255) column 255 left

      PostgreSQL counts characters; MySQL utf8mb4 counts them too, but the row limit is in bytes.

    What this tool does

    There is no single number called "the character count". This tool shows four at once — graphemes, code points, UTF-16 units and UTF-8 bytes — because for anything with an emoji, an accent or a non-Latin script they disagree, and not shipping a broken form comes down to knowing which one the system on the other side checks.

    Alongside the counts it works out SMS segments and how much room is left against the limits people hit most: X, meta descriptions, search-result titles, Instagram captions and a varchar(255) column.

    Common uses

    • Confirming a message fits one SMS segment before a carrier bills you for three.
    • Trimming a meta description to roughly 160 characters, or a title tag to roughly 60.
    • Finding out why a 200-character bio is rejected by a database column that nominally accepts 255.
    • Checking a translated UI string against the byte limit of a protocol field.

    A short example

    Take the four-person family emoji, 👨‍👩‍👧‍👦. Every counter below is measuring the same single symbol:

    Graphemes      1     one thing a reader sees
    Code points    7     4 people + 3 zero-width joiners
    UTF-16 units  11     each person is a surrogate pair
    UTF-8 bytes   25     4×4 bytes + 3×3 bytes

    Paste it into a field with a "10 character" limit and it may be accepted, silently truncated into two separate people, or rejected — depending entirely on which of those four numbers the validator uses.

    Why emoji make the numbers diverge

    A grapheme is one user-perceived character: é, 🇬🇧, 👍🏽. It is what a person means by "character", computed here with Intl.Segmenter.

    A code point is one Unicode scalar. The family emoji is seven of them: four people glued together with three U+200D zero-width joiners. Strip those joiners and the same bytes render as four separate people, which is exactly what a naive truncation does when it cuts through the middle of the sequence.

    A UTF-16 unit is what JavaScript's "x".length returns. Anything above U+FFFF occupies two. Most client-side maxlength checks use this number, which is why one emoji can eat two of your 280 characters.

    A UTF-8 byte is what a database column, an HTTP header or a protocol buffer counts. ASCII is one byte; Latin-1 accents two; most CJK three; emoji four.

    Worth knowing

    X counts none of these four ways. It uses weighted code points: Latin, Greek and Cyrillic count as one, everything else — CJK, Arabic, emoji — counts as two, and every URL counts as exactly 23 whatever its length. The 280 figure here uses graphemes, so treat it as a guide.

    SMS is stranger still. A GSM-7 message holds 160 characters, but one character outside the GSM 03.38 alphabet forces the whole message into UCS-2 at 70. A curly apostrophe pasted from a word processor does it, which is why a 140-character message sometimes bills as two.

    Frequently asked questions

    Why do two strings that look identical count differently?

    Unicode can encode the same text more than one way. é is either U+00E9 or a plain e followed by the combining acute accent U+0301 — one code point or two, two UTF-8 bytes or three — and a direct === comparison of the two forms is false. Normalisation (Unicode Annex #15) settles it: call .normalize('NFC') on both sides before comparing, hashing or storing. The grapheme figure is 1 either way.

    Why is my 200-character bio rejected by a varchar(255) column?

    Because the column may not be counting characters at all. PostgreSQL and MySQL size varchar(255) in characters, but SQL Server’s varchar(255) is 255 bytes, and Oracle’s VARCHAR2(255) defaults to byte semantics unless it is declared with CHAR. An accented or emoji-heavy 200-character bio can be over 400 bytes. Against those systems, read the UTF-8 byte figure above rather than the grapheme one.

    Is it safe to truncate text with slice(0, 100)?

    Not for arbitrary input. String.prototype.slice cuts by UTF-16 code unit, so it can land between the two halves of a surrogate pair and leave an unpaired surrogate behind. That value renders as a replacement character, has no valid UTF-8 encoding, and makes encodeURIComponent throw URIError: URI malformed. Use Array.from to slice by code point, or Intl.Segmenter when the limit is meant to match what a reader sees.