Zero Width Space and the Other Invisible Unicode Characters

The zero width space U+200B and its relatives: what each invisible character is for, which survive a paste, how to find them, and how to strip them from text.

Character blocks8 min read
The letters a and b with a zero width space between them, which draws nothing at all

U+200B ZERO WIDTH SPACE is a character that takes up no horizontal space and draws nothing. It is not a space, not an empty string, and not nothing โ€” it is a real character that exists in the text, counts against character limits and survives copy and paste.

It also has a dozen relatives, each doing a different job, and knowing which is which is the difference between a layout trick that works and one that silently fails.

What the zero width space is for

Its actual purpose is line breaking. In text with no spaces โ€” a long URL, a compound German word, Thai or Khmer, which write without word separators โ€” U+200B marks a point where a line may break, without putting any visible gap there.

https://a-very-long-host.test/<wbr>deeply/<wbr>nested/<wbr>path

In HTML the <wbr> element does the same job, and is the better choice on a web page because it cannot be accidentally copied into a search field. U+200B is for plain text, where no markup is available.

That is what it is for. What it gets used for is almost entirely different.

The invisible characters, compared

Not all of these are zero-width, and not all of them are reliable. The distinction that matters most is the last column: whether a system that trims or collapses whitespace will leave the character alone.

CharacterCode pointWidthSurvives trimming?
Zero Width SpaceU+200BnoneOften stripped
Zero Width Non-JoinerU+200CnoneUsually
Zero Width JoinerU+200DnoneUsually โ€” but see below
Word JoinerU+2060noneUsually
Zero Width No-Break SpaceU+FEFFnoneOften stripped as a BOM
Braille Pattern BlankU+2800one cellYes โ€” most reliable
Hangul FillerU+3164one cellYes
No-Break SpaceU+00A0one spaceSometimes
Narrow No-Break SpaceU+202FnarrowUsually
En SpaceU+2002half emUsually
Em SpaceU+2003one emUsually
Three-Per-Em SpaceU+2004third emUsually
Figure SpaceU+2007digit widthUsually
Ideographic SpaceU+3000full widthYes
Soft HyphenU+00ADnone, unless it breaksVaries

The invisible character tool has all of these with copy buttons, and a reveal mode that shows you which ones are hiding in text you paste into it.

Why the braille blank wins

U+2800 BRAILLE PATTERN BLANK is a braille cell with no dots raised. It is not classified as whitespace by Unicode โ€” its general category is So, a symbol โ€” so code that trims, collapses or rejects whitespace does not touch it. It occupies a cellโ€™s width, which is often exactly what you want, and it is covered by essentially every font.

That combination makes it the practical choice for an โ€œemptyโ€ line in an Instagram bio, a blank-looking message, or a name that appears to be nothing. The zero width space fails at all three on most platforms, because most platforms trim it.

The zero width joiner is not decoration

U+200D deserves a warning. It is a functional character: it tells the renderer to join what is either side of it. Modern emoji depend on it โ€” ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง is three people and two joiners, and ๐Ÿณ๏ธโ€๐ŸŒˆ is a flag joined to a rainbow.

Insert one between ordinary letters and most of the time nothing happens. Insert one next to an emoji and you may change which emoji is displayed. Do not use it as a generic invisible character.

What people use them for

Blank-looking names and messages. A field that rejects an empty value accepts a single invisible character. This works on many platforms and is explicitly blocked on some, and where it is blocked, the block is usually on U+200B specifically.

Spacing in bios. Instagram and TikTok trim empty lines. One braille blank on the line makes it non-empty, so the gap survives. See Instagram bio line breaks โ€” and note that most people who reach for this do not actually need it, because their real problem is the return key, not trimming.

Defeating naive filters. Inserting invisible characters inside a word breaks exact-match keyword filters. This works, and it is the reason platforms filter these characters in the first place โ€” it is a well-known evasion technique, and treating it as a clever trick tends to end with an account restriction.

Watermarking. A pattern of invisible characters embedded in a document identifies which copy a leak came from. This is a real technique in use, which is worth knowing if you paste text from a source that might be doing it.

Preventing autolinking. A zero width space inside a URL stops chat clients turning it into a clickable link.

The security side

Invisible characters are a genuine attack surface, and defensive filtering exists for good reasons.

Homograph and spoofing attacks. Invisible characters can make two visibly identical strings compare unequal, or make a malicious string look like a safe one. Unicode Technical Standard #39, the security profile, exists for exactly this and recommends restricting identifiers to a safe subset.

Trojan Source. In 2021, researchers showed that bidirectional control characters could make source code display in an order different from how a compiler reads it โ€” so reviewed code and compiled code differ. Compilers and code hosts now warn about it.

Filter evasion. As above: it works, which is why it is detected.

The practical upshot for a developer: strip invisible characters from anything used as an identifier, and be very cautious about accepting them in usernames, email addresses, file names and search queries.

Finding and removing them

Invisible characters are hard to debug precisely because nothing is there to see. A few approaches.

Reveal them. The invisible character tool lists every one it finds in pasted text, by name and position. Most code editors can also show them โ€” VS Code highlights ambiguous and invisible characters by default now.

Count them. If a stringโ€™s length does not match what you can see, something invisible is in it. The character counter shows characters, code points and bytes.

Strip them in code:

// the common zero-width set plus the BOM
text.replace(/[\u200B-\u200D\u2060\uFEFF]/g, '');

// wider: every format character (category Cf)
text.replace(/\p{Cf}/gu, '');

// normalise unusual spaces to a plain space
text.replace(/[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g, ' ');

Be deliberate about which set you strip. Removing all of category Cf also removes the zero width joiner, which will break emoji sequences, and the bidirectional marks, which will break Arabic and Hebrew text. For user-facing content, strip the zero-width set and leave the rest.

A note on normalisation

Unicode normalisation does not remove these characters. Not NFC, not NFD, and not even NFKC โ€” with one exception: NFKC converts U+00A0 NO-BREAK SPACE and most of the width-varying spaces to a plain space, but leaves the zero-width ones exactly where they are. They are meaningful characters in the scripts that need them, so the standard cannot discard them.

Removing them is always an explicit filtering step. See unicode normalisation for what the forms do change.

Quick answers

What is the zero width space used for?
Marking a permitted line break in text that has no spaces.

Is a zero width space the same as an empty string?
No. It is a character. It has a length, it costs bytes, and it survives copy and paste.

Which invisible character is most reliable?
U+2800 BRAILLE PATTERN BLANK, because it is not classified as whitespace and so survives trimming.

Will invisible characters get me banned?
Not by existing. Using them to evade content filters will, on most platforms.

How do I type one?
You copy it. There is no key for it, which is what the invisible character tool is for.

Do they count against character limits?
Yes, every one of them.