How to Remove Zero-Width Characters
Zero-width characters should be removed selectively, because some are accidental hidden characters while others are required for emoji, language shaping or line-breaking behavior.
On this page
Zero-width cleanup at a glance
| Character | Code point | Common name | Remove globally? |
|---|---|---|---|
| U+200B | ZERO WIDTH SPACE | No; often removable in plain text or identifiers after review |
| U+200C | ZERO WIDTH NON-JOINER | No; may be required by language |
| U+200D | ZERO WIDTH JOINER | No; often required for emoji and shaping |
| U+2060 | WORD JOINER | No; affects line breaking |
| U+FEFF | ZERO WIDTH NO-BREAK SPACE / BOM | Context-dependent |
| U+00AD | SOFT HYPHEN | Often removable from extracted plain text after review |
️ | U+FE0F | VARIATION SELECTOR-16 | No; can change emoji presentation |
The safest rule is to remove specific code points under a documented cleanup policy, not every invisible character.
What counts as a zero-width character?
The phrase zero-width character is informal. People often use it for U+200B ZERO WIDTH SPACE, U+200C ZERO WIDTH NON-JOINER, U+200D ZERO WIDTH JOINER, U+2060 WORD JOINER, legacy U+FEFF, variation selectors, combining marks that have no width by themselves, bidirectional controls and other default-ignorable format characters. These characters do not share one purpose. Some affect joining, line breaking, emoji presentation or direction; others are accidental artifacts. Start with What Are Invisible Unicode Characters? and inspect exact values with Unicode Character Inspector.
Detect before you remove
- Preserve the original text.
- Inspect code points.
- Identify exact Unicode names.
- Determine the text context.
- Check whether the character affects emoji or script shaping.
- Preview the cleanup.
- Compare before and after.
- Apply to a copy.
- Test collisions and meaning.
- Keep undo or rollback support.
A hidden character that is safe to remove from an ASCII username may be essential in a multilingual name or emoji sequence. Use Invisible Character Detector, Unicode Sequence Analyzer and Unicode Text Compare.
How to remove zero-width space U+200B
U+200B ZERO WIDTH SPACE marks an optional break opportunity. It can come from websites, rich-text editors, PDFs, word processors, CMS formatting, messaging applications or line-breaking logic. After review it is commonly removed from ASCII-only usernames, product codes, email fields, numeric identifiers, search keys, copied plain prose and generated slugs.
Before:
username
Code points:
U+0075 U+0073 U+0065 U+0072 U+200B U+006E U+0061 U+006D U+0065
After:
usernameLanguage context still matters because zero-width break opportunities can support text that does not use visible spaces. Remove U+200B only when your field or cleanup profile explicitly disallows it.
Why you should not remove U+200D blindly
U+200D ZERO WIDTH JOINER is used in emoji ZWJ sequences, composite family emoji, profession emoji, script shaping and joining behavior. The family emoji 👨👩👧👦 is conceptually MAN, ZWJ, WOMAN, ZWJ, GIRL, ZWJ, BOY. Before removal it may display as 👨👩👧👦; after removing joiners it may display as 👨👩👧👦, depending on platform support.
Why you should not remove U+200C blindly
U+200C ZERO WIDTH NON-JOINER prevents character joining, can preserve expected shaping and can be meaningful in writing systems that use joining behavior. Removal may change appearance, spelling, meaning, search behavior or identifier identity. U+200C must be governed by language and identifier policy, not by a universal removal rule.
Zero-width space vs word joiner
| Character | Code point | Behavior |
|---|---|---|
| ZERO WIDTH SPACE | U+200B | Allows a possible break |
| WORD JOINER | U+2060 | Prevents a break |
| NO-BREAK SPACE | U+00A0 | Adds spacing and prevents a break |
U+200B and U+2060 have opposite line-breaking roles. Removing U+2060 can allow unwanted wrapping, and converting U+2060 to U+200B is incorrect.
How to handle U+FEFF
U+FEFF ZERO WIDTH NO-BREAK SPACE may appear at the beginning of a text stream as a byte order mark or encoding signature. UTF-8 BOM bytes are EF BB BF, though UTF-8 does not need a BOM for byte order. Inside text, U+FEFF may be a legacy no-break character or accidental artifact. Do not remove it from raw bytes before determining whether it is a BOM. A decoded leading U+FEFF is often stripped from content, but mid-string use requires review. Use UTF-8 Validator and read What Is Mojibake? or How to Fix Broken UTF-8 Text.
Soft hyphens and copied text
U+00AD SOFT HYPHEN is not a zero-width joiner, but it is often cleaned with hidden characters. In international, the soft hyphen may remain invisible until wrapping. It can appear in PDF and web copy, affect equality and code-point counts, and break identifiers. It is often removable from extracted plain prose, but may be meaningful in typesetting or publishing. Keep soft hyphen removal as a named option.
Do not remove variation selectors blindly
U+FE0E VARIATION SELECTOR-15 and U+FE0F VARIATION SELECTOR-16 are invisible presentation selectors. In ♥ versus ♥️, the base character is U+2665, while the emoji-style form is U+2665 U+FE0F. Removing the selector may change appearance and can alter one grapheme cluster. It should not be treated as an accidental zero-width space.
Combining marks are not zero-width junk
Combining marks modify a base character. The sequence e + ◌́ is U+0065 U+0301. A combining mark may have zero advance width, but it is not equivalent to U+200B. Removing it changes text, and accent stripping is a separate destructive transformation. Unicode normalization and NFC vs NFD explain why normalization does not necessarily remove marks. Use Unicode Normalizer for inspection.
Bidirectional controls require a separate policy
Bidi controls such as U+202A–U+202E, U+2066–U+2069, U+200E and U+200F can be invisible and affect visual order. They are not ordinary zero-width spaces. Some mixed-direction text needs them, but unexpected overrides are risky in identifiers and source code. Detection and review are safer than global deletion, and security-sensitive fields should restrict them explicitly. Inspect them with Invisible Character Detector.
Interactive zero-width cleanup tool
This embedded tool uses the same local invisible-character analysis data as the detector, then adds cleanup previews, profile actions, undo and diagnostic reports. It never changes text on load.
Zero-width cleanup preview
Detect tracked hidden characters, preview cleanup profiles and apply changes only after review.
Limit: 2,000 UTF-16 code units. The text is not sent to the server.
Diagnostic original
Cleaned preview
| Select | Index | Code point | Unicode name | Risk | Default action | UTF-8 bytes |
|---|
How to remove zero-width characters from copied text
- Paste the text into the detector.
- Reveal invisible characters.
- Identify
U+200B,U+00AD,U+00A0andU+FEFF. - Preserve joiners and variation selectors by default.
- Preview line breaks and emoji.
- Apply the copied-text profile.
- Compare original and cleaned text.
- Copy the cleaned result.
- Keep the original until verified.
Plain copied-text cleanup may remove accidental U+200B, remove soft hyphens, convert NBSP to ordinary spaces and remove a stray leading BOM. It should not blindly remove ZWJ, ZWNJ, variation selectors, combining marks or bidi controls.
How to remove zero-width characters from usernames
Usernames and identifiers need a documented policy before account creation. Normalize according to that policy, detect zero-width and default-ignorable characters, reject U+200B where disallowed, restrict joiners to approved language contexts, detect mixed scripts and confusables, generate a comparison key, check collisions, preserve original input for review and show clear validation errors. Never clean an existing username silently if the result could collide with another account. Use Confusable Character Detector, Mixed Script Detector and Unicode Normalization Checker.
How to remove zero-width characters from email and URL fields
Hidden characters may invalidate an address or alter a target. URL parsing and email validation must use standards-aware libraries, not regex replacement as the full validation strategy. Reject unexpected controls before parsing, display the suspicious code point and preserve the original for user correction.
Input
→ reveal code points
→ reject disallowed hidden characters
→ normalize only if policy requires it
→ parse using a standards-aware libraryHow to remove zero-width characters from CSV and imports
Hidden characters can appear in headers and data cells, break joins and deduplication, survive spreadsheet export and create duplicate values after cleaning. Preserve the original file, scan all values, report code points by column, group occurrences by character type, apply column-specific rules, preview collision counts, write a new file and audit changed cells.
row_number
column_name
original_value
cleaned_value
removed_code_points
cleanup_profile
collision_detectedHow to remove zero-width characters from databases
Database cleanup may change uniqueness, indexes, foreign-key data and search results. Back up the database, identify affected columns, scan and count hidden characters, generate proposed cleaned values, detect collisions, store results in staging, review high-risk characters separately, update transactionally, preserve audit data and rebuild affected search indexes when needed.
value_original
value_cleaned
removed_code_points
cleanup_profile
collision_status
review_statusHow to audit zero-width characters in source code
Make hidden characters visible in the editor, use linter or compiler security checks, flag bidi controls, flag zero-width characters outside approved strings or comments, review identifiers containing default-ignorable characters and preserve valid Unicode in intended string literals. Do not automatically rewrite source code without a diff.
Scan
→ report file, line, column and code point
→ classify context
→ review
→ apply explicit patchRemoving zero-width characters in JavaScript
function removeZeroWidthSpace(text) {
return text.replaceAll("\u200B", "");
}const PLAIN_TEXT_REMOVALS = /[\u00AD\u200B\uFEFF]/gu;
function cleanCopiedPlainText(text) {
return text
.replace(PLAIN_TEXT_REMOVALS, "")
.replaceAll("\u00A0", " ");
}const CHARACTER_LABELS = new Map([
[0x00AD, "SOFT HYPHEN"],
[0x200B, "ZERO WIDTH SPACE"],
[0x200C, "ZERO WIDTH NON-JOINER"],
[0x200D, "ZERO WIDTH JOINER"],
[0x2060, "WORD JOINER"],
[0xFEFF, "ZERO WIDTH NO-BREAK SPACE"],
]);
function findTrackedCharacters(text) {
const matches = [];
let codeUnitIndex = 0;
for (const character of text) {
const codePoint = character.codePointAt(0);
if (CHARACTER_LABELS.has(codePoint)) {
matches.push({
codeUnitIndex,
character,
codePoint: `U+${codePoint.toString(16).toUpperCase().padStart(4, "0")}`,
name: CHARACTER_LABELS.get(codePoint),
});
}
codeUnitIndex += character.length;
}
return matches;
}Use exact code-point lists, preserve the original, recheck grapheme segmentation and test emoji and multilingual text. trim() is not enough.
Removing zero-width characters in Python
def remove_zero_width_space(text: str) -> str:
return text.replace("\u200b", "")PLAIN_TEXT_REMOVALS = {
"\u00ad", # SOFT HYPHEN
"\u200b", # ZERO WIDTH SPACE
"\ufeff", # ZERO WIDTH NO-BREAK SPACE
}
def clean_copied_plain_text(text: str) -> str:
cleaned = "".join(
character
for character in text
if character not in PLAIN_TEXT_REMOVALS
)
return cleaned.replace("\u00a0", " ")import unicodedata
TRACKED_CODE_POINTS = {0x00AD, 0x200B, 0x200C, 0x200D, 0x2060, 0xFEFF}
def find_tracked_characters(text: str) -> list[dict[str, object]]:
result = []
for index, character in enumerate(text):
code_point = ord(character)
if code_point in TRACKED_CODE_POINTS:
result.append({
"index": index,
"character": character,
"code_point": f"U+{code_point:04X}",
"name": unicodedata.name(character, "<unnamed>"),
"category": unicodedata.category(character),
})
return resultDo not remove all Cf characters. Preserve U+200C and U+200D by default, keep original values during bulk cleanup and use grapheme-aware testing where user-visible text matters.
Removing zero-width characters in PHP
function removeZeroWidthSpace(string $text): string
{
return str_replace("\u{200B}", "", $text);
}function cleanCopiedPlainText(string $text): string
{
$cleaned = str_replace(
["\u{00AD}", "\u{200B}", "\u{FEFF}"],
"",
$text
);
return str_replace("\u{00A0}", " ", $cleaned);
}function findTrackedCharacters(string $text): array
{
$tracked = [0x00AD, 0x200B, 0x200C, 0x200D, 0x2060, 0xFEFF];
$characters = mb_str_split($text, 1, "UTF-8");
$result = [];
foreach ($characters as $index => $character) {
$codePoint = IntlChar::ord($character);
if (!in_array($codePoint, $tracked, true)) {
continue;
}
$result[] = [
"index" => $index,
"character" => $character,
"code_point" => sprintf("U+%04X", $codePoint),
"name" => IntlChar::charName($codePoint),
];
}
return $result;
}mbstring and intl may be required. PHP strings are byte sequences, so validate UTF-8 before Unicode-aware cleanup and do not use byte indexing.
Why regex-only cleanup is risky
A regex can remove specified code points, but it cannot determine language or emoji context by itself. Broad ranges remove legitimate formatting, and Unicode category Cf contains characters with different roles. The common pattern /[\u200B-\u200D\uFEFF]/gu removes U+200B, U+200C, U+200D and U+FEFF, so it can break language shaping and emoji. A short regex is useful only when its exact character list matches the documented policy.
Collision detection after cleanup
username and username can become the same value after U+200B removal. That can create duplicate accounts, unique-index failures, overwritten records, ambiguous product codes, conflicting filenames and broken external references. Generate cleaned candidates, group by cleaned value, flag groups with more than one original, review manually and never overwrite silently.
| Original value | Cleaned value | Collision |
|---|---|---|
username | username | Yes |
username | username | Yes |
How normalization interacts with zero-width cleanup
NFC and NFD do not generally remove U+200B, and normalization does not replace cleanup policy. NFKC may alter some compatibility characters, but it is not a hidden-character remover. Identifier workflows may normalize before checking disallowed code points, while display text may need original preservation. Normalize for canonical consistency, then apply field-specific hidden-character validation. Exact order depends on the governing specification. Use Unicode Normalization Checker.
Safe cleanup profiles
Copied plain text
Remove U+200B, remove U+00AD, remove stray leading U+FEFF and optionally convert U+00A0 to U+0020. Preserve ZWNJ, ZWJ, variation selectors, combining marks and bidi controls unless explicitly reviewed.
Strict ASCII identifier
Reject all non-ASCII rather than silently removing; report exact code points, offer user correction and check collisions.
Unicode identifier
Apply documented normalization, restrict default-ignorables, permit joiners only under approved rules, detect scripts and confusables and check collisions.
Emoji-preserving text
Preserve U+200D, U+FE0F, emoji modifiers, regional indicators and combining sequences.
Source-code audit
Reveal all invisible characters, flag bidi controls and joiners, and never auto-modify.
Common zero-width cleanup mistakes
Removing U+200B, U+200C and U+200D together
This can break valid language and emoji.
Using a broad Unicode category
Deleting all Cf characters is unsafe.
Assuming invisible means meaningless
Many invisible characters have semantic effects.
Cleaning after account creation
This can create identifier collisions.
Removing variation selectors
This can change emoji presentation.
Removing combining marks
This changes spelling and meaning.
Assuming normalization removes zero-width characters
It usually does not.
Using trim as the cleanup
Most format characters are unaffected.
Cleaning passwords
Passwords should not be silently changed.
Cleaning source code automatically
Require review and diffs.
Ignoring line-breaking behavior
Removing U+200B or U+2060 can change wrapping.
Failing to preserve original values
Bulk cleanup must be reversible.
Practical zero-width cleanup workflow
- Preserve the original.
- Reveal invisible characters.
- Identify exact code points.
- Classify risk.
- Choose a named cleanup profile.
- Preview changes.
- Recalculate grapheme clusters.
- Check emoji and language shaping.
- Check collisions.
- Apply to a copy.
- Review changed values.
- Record the policy and transformation.
- Keep rollback support.
- Add regression tests.
Use Invisible Character Remover, Unicode Character Inspector, Unicode Sequence Analyzer, Unicode Character Counter and Unicode Text Compare.
Try these UnicodeNow tools
These tools reveal hidden code points, compare before and after cleanup, and validate text before bulk changes.
Invisible Character Detector
Find zero-width, control, variation, private-use and spacing characters.
Invisible Character Remover
Remove hidden Unicode controls and zero-width characters from text.
Unicode Character Inspector
Inspect each Unicode character, encoding, category, script and normalization form.
Unicode Sequence Analyzer
Analyze code points, grapheme clusters, bytes, scripts and directionality.
Unicode Text Compare
Compare strings exactly and after Unicode normalization.
Unicode Character Counter
Count code points, grapheme clusters, words, bytes and invisible characters.
Unicode Normalization Checker
Check which Unicode normalization forms match the input.
Confusable Character Detector
Detect Unicode confusables, scripts, skeleton matches and suspicious lookalike patterns.
Clean Copied PDF Text
Repair common spacing, hyphenation and line-break issues from copied PDF text.
Frequently asked questions
How do I remove a zero-width space?
Remove the exact U+200B character after verifying that it is unwanted in the current field.
Can I remove all zero-width characters with one regex?
You can, but you should not. A broad regex may delete required ZWJ and ZWNJ characters.
Is U+200B safe to remove?
Often in ASCII identifiers or copied plain text, but not universally.
Is U+200D safe to remove?
No. It is required by many emoji sequences and some shaping behavior.
Is U+200C safe to remove?
Not globally. It may be linguistically significant.
What is the difference between U+200B and U+2060?
U+200B allows a break opportunity, while U+2060 prevents a break.
Should I remove U+FEFF?
A stray decoded leading U+FEFF may be removable, but raw BOM handling and mid-string usage require context.
Does trim remove zero-width characters?
Not all of them.
Does Unicode normalization remove U+200B?
Generally no.
Can removing zero-width characters change emoji?
Yes, especially when U+200D or variation selectors are removed.
Can cleanup create duplicate usernames?
Yes. Different original values can collapse to the same cleaned value.
Should I remove zero-width characters from passwords?
No, not silently. Password processing must follow the authentication specification exactly.
How do I remove zero-width characters from JavaScript text?
Use targeted replacement such as text.replaceAll("\u200B", "") when U+200B is explicitly disallowed.
How do I remove zero-width characters from Python text?
Use exact replacement such as text.replace("\u200b", "") under a documented policy.
How do I reveal zero-width characters before removing them?
Use an invisible-character detector that displays code points and Unicode names.
References
- The Unicode Standard
- Unicode Character Database
- Unicode glossary
- Unicode Standard Annex #29: Unicode Text Segmentation
- Unicode Standard Annex #14: Unicode Line Breaking Algorithm
- Unicode Standard Annex #9: Unicode Bidirectional Algorithm
- Unicode Standard Annex #15: Unicode Normalization Forms
- Unicode emoji data
- MDN: JavaScript strings
- Python documentation: unicodedata
- PHP manual: IntlChar
- ICU Unicode properties