How to strip or replace emojis in markdown
- Step 1Paste or upload your Markdown — Use the Paste text tab for a quick snippet, or Upload file to load a
.md,.mdx,.markdown, or.txtfile. The tool processes one file at a time. - Step 2Pick a mode — Leave Mode on Strip (remove) to delete emoji and shortcodes, or switch to Replace with [name] to keep bracketed placeholders where each glyph was.
- Step 3Decide about code blocks — Leave Process code blocks too unchecked to protect fenced ``
`` content. Check it only when you actually want emoji removed from code as well. - Step 4Run De-Emoji — Click Run De-Emoji. The transform happens in the browser; large pastes are bounded by your tier's character limit (500,000 on Free).
- Step 5Review the result — The cleaned Markdown appears in a read-only panel. Scan for the leftover-space cases (
word word) where an emoji sat between two words, and fix any double spaces if your renderer is whitespace-sensitive. - Step 6Copy or download — Use Copy to put the result on your clipboard or Download to save a
.mdfile. Paste it back into your repo, CMS, or doc pipeline.
Every option this tool exposes (and nothing it does not)
The De-Emoji panel has exactly two controls. There is no allow-list, no per-emoji picker, no 'keep meaningful emoji' toggle, and no name dictionary — these are the real defaults from lib/markdown/markdown-tool-schemas.ts.
| Control (UI label) | Field | Values | Default | Effect |
|---|---|---|---|---|
| Mode → Strip (remove) | mode | strip | Yes (default) | Deletes each matched emoji and each :shortcode: outright. Surrounding spaces are left in place — they are not collapsed. |
| Mode → Replace with [name] | mode | replace | — | Wraps each matched emoji in square brackets (the literal glyph stays inside, e.g. the rocket becomes [ rocket-glyph ]) and turns :rocket: into [rocket]. |
| Process code blocks too | includeCodeBlocks | true / false | false (off) | Off: fenced `` `` blocks are skipped so command-line emoji or test fixtures stay intact. On: emoji inside fences are processed identically to prose. |
What counts as an emoji (matched Unicode ranges)
The de-emoji pass matches these Unicode blocks, each optionally followed by the U+FE0F variation selector. Anything outside these blocks is left untouched — verified directly against the matcher in lib/markdown/markdown-processor.ts.
| Block / range | Examples it matches | What it does NOT catch | Why it matters |
|---|---|---|---|
| U+1F600–1F64F (Emoticons) | grinning, wink, crying, neutral faces | ASCII emoticons like :) :-( ^_^ (plain text, never matched) | Face emoji are the most common decorative noise in AI-drafted prose |
| U+1F300–1FAFF (Misc Symbols & Pictographs, Supplemental, Extended-A) | weather, food, animals, hands, objects, newer 2020+ emoji | Letterlike symbols (tm) U+2122, (c) U+00A9, (R) U+00AE | Trademark and copyright marks survive — they are not emoji |
| U+2600–27BF (Misc Symbols + Dingbats) | check mark, star, heart, warning, arrows-as-dingbats | Box-drawing, geometric shapes below U+2600, math operators | A literal check (check) in a feature list is treated as decorative and removed |
| U+1F680–1F6FF (Transport & Map) | rocket, car, plane, warning-triangle transport icons | Map pin variants outside the block | Common in README hero lines and roadmap bullets |
| U+1F900–1F9FF (Supplemental Symbols) | additional hands, gestures, body parts, fantasy | Skin-tone modifier in isolation is matched as its own token | Skin-tone + base render as two bracket tokens in Replace mode |
| U+1F1E6–1F1FF (Regional Indicators) | the two-letter pieces that combine into flags | The flag as a single grapheme is matched as two code points | A flag becomes two bracket tokens in Replace mode, not one |
Before / after by mode (verified output)
Exact transformations produced by the strip and replace passes. Note the leftover space after removal and the bracket-the-glyph behaviour in Replace mode.
| Input fragment | Strip output | Replace output |
|---|---|---|
Ship it (rocket-emoji) today | Ship it today (double space remains) | Ship it [rocket-glyph] today |
Done :white_check_mark: | Done (shortcode removed) | Done [white_check_mark] |
Status: :) | Status: :) (ASCII emoticon untouched) | Status: :) (ASCII emoticon untouched) |
(c) 2026 ACME (tm) | (c) 2026 ACME (tm) (marks survive) | (c) 2026 ACME (tm) (marks survive) |
thumb-emoji + skin-tone | `` (both code points removed) | [thumb-glyph][skin-glyph] (two tokens) |
Cookbook
Real before/after fragments. The (rocket), (check), and (warning) placeholders below stand in for the literal emoji glyphs you would paste.
Strip a single feature line
The everyday case: one emoji-decorated line headed for a plain-text destination. Note that Strip leaves the spaces that flanked the glyph, so a double space appears.
Mode: Strip Input: - (rocket) Fast cold starts - (check) Zero-config setup Output: - Fast cold starts - Zero-config setup (The leading list marker now has two spaces; tighten if needed.)
Replace mode keeps an audit trail
When a reviewer needs to know where emoji were, Replace wraps each glyph in brackets. For Unicode emoji the literal glyph stays inside the brackets; only :shortcodes: become a plain word.
Mode: Replace with [name] Input: Deploy (rocket) then verify :white_check_mark: Output: Deploy [(rocket)] then verify [white_check_mark] (The Unicode rocket is bracketed as-is; the shortcode becomes a word.)
Shortcodes that never rendered
GitHub renders :smile: as a glyph, but a plain CMS shows the literal text. Strip removes both styles so neither leaks through.
Mode: Strip Input: Thanks for shipping :tada: :+1: Output: Thanks for shipping (Both shortcodes gone; trailing spaces remain.)
Protect emoji inside a code fence
A shell example that prints an emoji must survive. With Process code blocks unchecked (the default), the fence is skipped.
Mode: Strip, Process code blocks: OFF Input: Celebrate (party) in chat: ```bash echo "Deployed (party)" ``` Output: Celebrate in chat: ```bash echo "Deployed (party)" ``` (Prose stripped; the fenced echo keeps its emoji.)
What is intentionally left alone
Marks and emoticons are not emoji and must not be touched. This is the expected, correct no-op.
Mode: Strip Input: (c) 2026 ACME (tm). Build status: :) -- shipping em-dash test Output: (c) 2026 ACME (tm). Build status: :) -- shipping em-dash test (Copyright, trademark, ASCII smiley, and dashes all preserved.)
Edge cases and what actually happens
Removed emoji leaves a double space
By designStrip mode deletes the emoji code point but does not collapse the whitespace around it. Ship it (rocket) today becomes Ship it today. If your renderer is whitespace-sensitive or you want tidy output, run the result through md-prettifier afterward, or do a quick double-space cleanup.
ASCII emoticons like :) are kept
Preserved:), :-(, ^_^, and <3 are plain ASCII, not emoji code points, so they are never matched. If you also need to remove typographic emoticons, that is a manual find/replace — this tool only targets the Unicode emoji blocks.
Trademark, copyright, registered marks survive
Preserved(tm) U+2122, (c) U+00A9, and (R) U+00AE fall outside the matched ranges, so they pass through unchanged. This is intentional — they are legal marks, not decoration.
A non-emoji `:word:` gets stripped too
ExpectedThe shortcode matcher is :word: with lowercase letters, digits, _, +, -. It cannot tell an emoji shortcode from any other colon-wrapped token, so a path fragment like /api/:id: or a Ruby symbol-ish :id: will also be removed/bracketed. Check colon-heavy technical text before trusting the output.
Flags and skin-tone emoji split into pieces
ExpectedA country flag is two regional-indicator code points and a skin-toned hand is a base emoji plus a modifier. The matcher works per code point, so in Replace mode a flag becomes two bracket tokens and a skin-toned hand becomes [base][modifier]. In Strip mode both pieces are removed, which is usually what you want.
Keycap sequences (1-keycap, hash-keycap) are not removed
PreservedEnclosed keycap emoji such as the digit-plus-keycap or hash-plus-keycap are built from an ASCII base, a variation selector, and U+20E3 (combining keycap), which is outside the matched ranges. The base character stays; you may see a leftover combining mark. Remove these manually if needed.
Newest emoji from a very recent Unicode release
Mostly coveredThe matcher covers up to the U+1FAFF Extended-A block, which includes the 2020-era additions. An emoji assigned in a bleeding-edge Unicode version above that range may slip through. If you spot one, it is outside the implemented ranges, not a bug in your input.
Input exceeds your character limit
LimitPasted text is capped per tier: Free 500,000 characters, Pro 5,000,000, Pro-media 20,000,000, Developer unlimited. Over the cap, the Run button is disabled and the counter turns red. Upload a file or split with md-splitter for very large documents.
Output looks unchanged
ExpectedIf nothing changed, your document had no matched emoji or shortcodes — only marks, dashes, accents, or ASCII emoticons, none of which are targeted. The pass is a safe no-op in that case.
Frequently asked questions
Does Strip mode also remove `:shortcode:` emoji?
Yes. Strip removes both real Unicode emoji and :name: shortcodes such as :smile: or :+1:. This is a deliberate part of the implementation — shortcodes that render as glyphs on GitHub but show as literal text elsewhere are both cleaned out.
What exactly does Replace mode produce?
It wraps each matched emoji in square brackets and converts shortcodes to a bracketed word. For a Unicode emoji the literal glyph stays inside the brackets (the rocket becomes [ rocket-glyph ]); for :rocket: it becomes [rocket]. The UI labels this 'Replace with [name]', which is exact for shortcodes.
Will it touch emoji inside code blocks?
Not by default. The 'Process code blocks too' checkbox is off, so fenced `` `` content is skipped and any emoji in a command or fixture survives. Check the box only when you want emoji removed from code as well.
Are `(tm)`, `(c)`, and `(R)` removed?
No. Trademark (U+2122), copyright (U+00A9), and registered (U+00AE) marks are outside the matched emoji ranges, so they are preserved. The tool targets emoji code points only.
What about ASCII emoticons like :) or <3?
They are kept. Those are ordinary ASCII characters, not Unicode emoji, so the matcher never sees them. Removing typographic emoticons is a separate manual step.
Why is there a double space after stripping?
Strip deletes the emoji but leaves the spaces that flanked it, so a (rocket) b becomes a b. Run the output through md-prettifier if you want whitespace tidied.
Is anything uploaded to a server?
No. The whole transform runs in your browser via the in-page engine. Nothing about your document is sent anywhere; only a usage counter (no content) is recorded if you are signed in.
Can it process many files at once?
No — this tool handles one document per run. For multi-file Markdown work, combine first with md-merger, clean, then re-split with md-splitter.
Does it accept anything other than .md?
Upload accepts .md, .mdx, .markdown, and .txt. Output is always Markdown text shown in a copy/download panel — there is no rendered preview for this tool.
Could it remove a non-emoji `:word:` by accident?
Yes, potentially. The shortcode pattern matches any lowercase :word:, so a path like /users/:id: could be affected. Review colon-heavy technical text, and if needed strip emoji on the prose only and patch code paths by hand.
Does Replace give human-readable names like [rocket] for Unicode emoji?
Only for shortcodes. A :rocket: becomes [rocket], but a literal Unicode rocket glyph is wrapped as [ glyph ] — the bracketed token keeps the original character rather than translating it to an English name. If you want named labels, write them as :shortcodes: first.
How big a document can I clean?
Pasted text is limited to 500,000 characters on Free, 5,000,000 on Pro, 20,000,000 on Pro-media, and unlimited on Developer; file uploads follow the 1 MB / 10 MB / 50 MB / 500 MB size tiers. The character limit is separate from the byte size, so a dense file can hit the char cap before the size cap.
Privacy first
All Markdown processing runs locally in your browser using JavaScript. No file is ever uploaded to JAD Apps servers — only metadata counters are saved for signed-in dashboard stats.