Remove Unicode variation selectors
Hello ❤️ world ☺️ loses its 2 hidden selectors and comes back as Hello ❤ world ☺, 15 characters instead of 17. Letters, emoji, punctuation and spaces stay; only the invisible style switches go. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Worked example
- Text
- Hello ❤️ world ☺️
- Result
- Hello ❤ world ☺
- Length
- 15
- Original length
- 17
- Removed
- 2
Unicode variation selectors are invisible characters that sit next to base glyphs and tell renderers whether to show emoji presentation, text presentation, or a specific glyph variant. Copy-paste from messaging apps, social feeds, and design tools often leaves U+FE0F (emoji style) or U+FE0E (text style) trailing hearts, stars, and other symbols, and CJK documents may embed Variation Selectors Supplement code points for ideographic variation sequences. Those selectors break string equality checks, inflate lengths, confuse search indexes, and produce mysterious diffs when two strings look identical on screen. This tool walks your text by Unicode code point, removes every variation selector in the standard ranges, and returns the cleaned string with a removed count and change flags. Base letters, emoji, punctuation, and spaces stay put—only the invisible toggles are stripped. Empty input is rejected with a clear validation error so clients notice missing fields.
How variation selector removal works
A variation selector is not a visible letter; it is a non-spacing code point that modifies the presentation of the preceding character. The main block is U+FE00 through U+FE0F: Variation Selectors 1 through 16. Within that block, U+FE0E requests text (monochrome) presentation for emoji-capable symbols, and U+FE0F requests emoji (color) presentation.
A heart character followed by FE0F often renders as a colorful emoji heart, while the same base alone or with FE0E may render as a plain dingbat, depending on the font and platform. A second block, U+E0100 through U+E01EF (Variation Selectors Supplement), supports ideographic variation sequences for CJK characters when a specific glyph shape is required. This capability removes both blocks completely.
Implementation walks the string with a code-point iterator so astral-plane Supplement characters are recognized as single units rather than split surrogate pairs. Each code point in either VS range is discarded and counted; every other code point is appended to the result. Empty strings fail before the walk.
Because the path is pure parse work over strings, latency stays sub-millisecond for typical form sizes and the published cost model is analytic rather than model-token based.
Where remove-variation-selectors tools show up in real work
Product and messaging teams normalize user-generated content so equality checks and duplicate detection ignore invisible presentation toggles that users never intended to type. Search and tagging systems strip variation selectors from query strings and stored titles so filters match regardless of whether a heart arrived with FE0F from iOS or without it from a plain text editor.
Content and localization teams prepare stable strings for filenames, URL segments (when not using a dedicated slug tool), email subjects on picky relays, and CSV files opened in tools that mangle or re-encode Unicode. If you later need zero-width character removal, diacritic stripping, full Unicode normalization (NFD/NFC), or emoji shortcode conversion, treat those as separate search intents and landings rather than overloading this remove-variation-selectors page.
What you can do with it
Normalize emoji from chat and social APIs
Strip FE0F/FE0E presentation selectors so hearts and symbols compare equal across platforms that inject different variation styles.
FAQ
What does remove variation selectors do?
It strips Unicode variation selector code points (U+FE00–U+FE0F and U+E0100–U+E01EF) from your text—including the common FE0F emoji and FE0E text presentation selectors—and returns the cleaned string plus a removed count.
What happens if I send empty text?
Empty strings are rejected so clients notice missing fields instead of receiving a silent empty result.
Does it remove zero-width spaces or accents?
No. Only variation selectors are removed. Zero-width spaces, bidi marks, combining diacritics, and other invisible characters need separate tools.
Will already-clean text change?
No. If there are no variation selectors, result equals the original text, changed is false, and removed is zero.
What does it cost?
The browser tool is free and runs locally.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/str/remove-variation-selectors \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"Hello ❤️ world ☺️"}'const res = await fetch("https://api.kit.forhosting.com/str/remove-variation-selectors", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text": "Hello ❤️ world ☺️"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/str/remove-variation-selectors",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text": "Hello ❤️ world ☺️"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/str/remove-variation-selectors", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text":"Hello ❤️ world ☺️"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text":"Hello ❤️ world ☺️"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/str/remove-variation-selectors", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"text": "Hello ❤️ world ☺️"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "str.remove_variation_selectors",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
Technical notes
How variation selector removal works
There is no locale switch, no random sampling, and no network call—identical input always yields identical JSON. Payloads longer than the documented character cap also fail with invalid_input so a single request cannot monopolize Worker memory.
What you send and what you get back
Send a single required field named text containing the non-empty string you want to clean. Numbers and booleans are coerced to their decimal or true/false text forms; objects, arrays, NaN, and Infinity are rejected. Classic checks include a heart with trailing FE0F becoming the bare heart, a smiley with FE0F losing the presentation selector while keeping the base emoji, mixed prose where only the invisible selectors disappear, and CJK text with Supplement selectors reduced to base ideographs. Aliases such as input, value, data, s, message, string, content, and body are accepted when wiring older clients. On success the response echoes the original text, the cleaned result, the result length, the original length, a boolean changed flag, removed as the count of variation selector code points stripped, and operation set to remove_variation_selectors. Wire the free browser form for quick paste-and-copy work, then call the identical contract from scripts when you normalize bulk CSV columns, chat exports, or API payloads. Pricing for successful API calls is the published base of $0.002 per item; validation failures including empty text do not consume prepaid balance.
Where remove-variation-selectors tools show up in real work
Data engineers cleaning CRM exports, spreadsheet columns, and social API dumps often force free-text fields through a VS strip before joining on soft keys or loading into warehouses that index by exact string. Because this capability is a pure parse engine, API pricing is the published base of $0.002 per successful item and failed validation does not consume balance.
Integration use cases
Clean CSV and free-text exports
Remove hidden VS characters from bulk paste and API dumps before joins, uniqueness checks, or warehouse loads.
Stable golden tests and search keys
Make fixtures and facet labels independent of invisible presentation toggles so tests and filters stay deterministic.