The zero-width family
U+200B ZERO WIDTH SPACE: a break opportunity with no width. The canonical invisible character and the one most people mean when they say "hidden character".
U+200C ZERO WIDTH NON-JOINER: prevents two characters from joining. Load-bearing in Persian and Arabic, and in several Indic scripts.
U+200D ZERO WIDTH JOINER: forces two characters to join. This is what turns separate emoji into a single family or profession glyph.
U+2060 WORD JOINER: prevents a line break at that position. No width, no break opportunity. Behaves like U+FEFF but is the character actually intended for the job.
U+FEFF ZERO WIDTH NO-BREAK SPACE: the byte-order mark. Legitimate as the very first character of a file; anywhere else it is leftover junk or a deliberate marker.
U+180E MONGOLIAN VOWEL SEPARATOR: reclassified as a format character in Unicode 6.3 and now effectively zero-width.
How they get into your text
Web typography is the biggest single source. Sites insert zero-width spaces to control where long words and URLs break, and those characters come along when you copy.
PDF extraction inserts them at line-break positions, which is why text copied out of a PDF often has them at seemingly random intervals mid-word.
AI assistant output contains them with some regularity, particularly after markdown rendering.
Deliberate marking, meaning an identifier encoded as a pattern of zero-width characters, is real but rarer than the accidental sources. The pattern is usually obvious in the position preview: deliberate marks tend to be evenly distributed or clustered at boundaries, while accidental ones follow line-break positions.
The joiner problem
U+200C and U+200D are in this family but are not always junk, which is what makes blanket removal a bad idea.
A zero-width joiner sitting between two emoji is holding a single glyph together. Remove it and 👨👩👧 becomes three unrelated people. A zero-width non-joiner between two Persian letters is part of the spelling. Remove it and the word is misspelt.
This tool looks at what is on either side before deciding. Between two emoji bases, the joiner stays. Between two Latin letters, it goes. Between two letters of the same joining script, it stays. There is a checkbox to strip them regardless, and the tool tells you what it kept and why.