Removing accents from text in JavaScript reliably takes more than calling String.prototype.normalize('NFD') and stripping the resulting combining marks, because the Unicode Character Database records no canonical decomposition for a documented family of Latin letters such as ø, ł, đ, þ, ß, æ, œ, đ, ħ, ŧ and the dotless ı. A pure normalize-and-strip pipeline silently leaves every one of them untouched, so a city called Łódź stays Łódź instead of becoming Lodz, and a CSV column reading "São Paulo,Łódź,Quito" can match neither "Sao Paulo,Lodz,Quito" nor itself across exports. The reason is structural: characters such as ø or ł have no base-plus-mark form, so there is no combining sequence for a regex like /\p{M}/gu to find, and the script returns before doing anything useful. The reliable path in JavaScript is a layered implementation: NFD normalization to catch true diacritics such as é and ç, an explicit fold table for the non-decomposing letters, a separate toggle for transliterations such as æ→ae and ß→ss, and strict guards so Greek, Cyrillic, Chinese, Japanese, Korean, Devanagari and emoji pass through untouched. The Remove Accents from Text tool bakes that exact pipeline into a single paste-and-click page, and the rest of this article walks through what each layer has to do, what NFD misses, and how to use the tool without having to write, test and maintain the whole thing yourself.

Why JavaScript's Normalize-and-Strip Approach Breaks on Real Input
The recipe that turns up first in almost every JavaScript tutorial looks something like this:
str.normalize('NFD').replace(/\p{M}/gu, '')
That two-step pattern is genuinely correct for the diacritic it was designed for. A precomposed é (U+00E9) decomposes under NFD into the base letter e plus the combining acute accent U+0301, and the second step deletes the combining mark, leaving a clean e. The same path works for ç, ü, ñ, ï, ÿ and the whole family of letters that Unicode defines as a base glyph plus a stacked mark, which is exactly what the Unicode normalization rules in UAX #15 were written to support. JavaScript's String.prototype.normalize() method implements those rules faithfully, and the \p{M} Unicode property escape catches every standard combining mark category.
The script then moves on, the unit test for café passes, and the bug surfaces a week later when a Polish address, an Icelandic name or a German product title suddenly fails to match. The script did exactly what was asked of it; what was asked was simply not enough. Characters such as ø, ł, đ, ħ, ŧ, the dotless ı, ð and Ð carry no separable accent at all. They are atomic code points in the Unicode Character Database, with no decomposition mapping into a base letter plus a combining mark. NFD therefore leaves them unchanged, the regex finds nothing to remove, and the final string contains the same letter the input started with. For a tool that advertises "accent removal," that is the silent failure mode.
The Letters NFD Normalization Cannot Touch
The gap between what NFD catches and what real-world text contains is wide enough to break real workflows. The table below lists the Latin letters that most often cause problems, what a plain normalize-and-strip script does with each, and what a complete implementation should map them to. The transliterations (æ→ae, ß→ss, þ→th and their capitals) are deliberately separated from the diacritic removals and live behind their own toggle, because they replace one letter with two and a user cleaning Polish text for a slug usually wants them while a linguist preserving Old English spelling usually does not.
| Input letter | NFD strips the diacritic? | Correct fold | Classification |
|---|---|---|---|
| é (U+00E9) | Yes | e | Diacritic |
| ç (U+00E7) | Yes | c | Diacritic |
| ü (U+00FC) | Yes | u | Diacritic |
| ø (U+00F8) | No | o | Non-decomposing letter |
| ł (U+0142) | No | l | Non-decomposing letter |
| đ (U+0111) | No | d | Non-decomposing letter |
| ð (U+00F0) | No | d | Non-decomposing letter |
| þ (U+00FE) | No | th | Transliteration |
| ß (U+00DF) | No | ss | Transliteration |
| æ (U+00E6) | No | ae | Transliteration |
| œ (U+0153) | No | oe | Transliteration |
| ħ (U+0127) | No | h | Non-decomposing letter |
| ı (U+0131) | No | i | Non-decomposing letter |
Every entry in the "Correct fold" column above has been checked against the official UnicodeData.txt file. The transliterations are not Unicode-defined mappings; they are documented spelling choices, which is exactly why the Remove Accents from Text page separates them on principle rather than blurring the line between diacritic removal and re-spelling.
How to Remove Accents From Text in JavaScript the Right Way
For most JavaScript developers, the fastest way to ship accent-free output without having to build and maintain the layered pipeline above is to use a tool that already has it. The Remove Accents from Text page runs NFD, applies the verified fold table, exposes the transliteration toggle, and guards every non-Latin script in a single browser-based pass. Here is the workflow.
- Paste the text that contains accented or special Latin characters into the input area. Up to one million characters is processed in a single linear pass, so even large CSV exports and address lists return instantly.
- Choose the transliteration toggle for ligatures and letters such as æ, œ, ß and þ. Leave it on for slug generation, contact-list deduplication and search indexing; switch it off when you need to preserve original spellings such as German "Straße" or Old English "Þorri."
- Choose whether to keep or strip non-Latin scripts. The default preserves Greek, Cyrillic, Chinese, Japanese, Korean, Devanagari and emoji byte-for-byte; enable strict mode only when you genuinely need ASCII-only output.
- Click Remove accents. The page applies NFD, strips combining marks from Latin base letters only, folds the non-decomposing letters through the table, applies the toggle rules, then recomposes to NFC so the output is identical regardless of how the input was stored.
- Check the change count reported on the page to confirm how many characters were rewritten, then copy the cleaned result back into your JavaScript pipeline, your CSV file or your slug field.
Because the operation is idempotent, running the same input through the tool twice produces the same output as running it once. That property matters when the cleaned text is fed into another cleaning step, and it is exactly why recomposition to NFC is part of the contract: a precomposed é stored as U+00E9 and a decomposed é stored as e followed by U+0301 both arrive at the same single code point after the page finishes, and either input shape produces the same cleaned string.
What a Real Accent Remover Must Refuse to Touch
The opposite failure mode is just as common and just as silent. Greek letters such as ά (alpha with acute), έ (epsilon with acute) and ή (eta with acute) decompose in Unicode the same way accented Latin does: base letter plus a combining mark. Cyrillic letters behave the same way, and so do the Indic scripts whose vowel signs sit above or below a base consonant. A naive stripper that deletes every \p{M} it finds will quietly rewrite ά to α, ё to е, and a Devanagari vowel sign to nothing, corrupting text in scripts it was never asked to clean.
The Remove Accents from Text tool only removes marks attached to Latin base letters. Greek, Cyrillic, Chinese, Japanese, Korean and Devanagari pass through byte-for-byte, and Devanagari vowel signs, which are grammatically letters rather than accents, are never treated as removable marks. Emoji pass through in the same way, which matters any time your input field accepts user-generated content. An optional strict mode can delete every non-Latin character outright when you truly need ASCII-only output, but it is off by default because that is the destructive choice and the page lets the user make it explicitly.
When You Actually Need ASCII-Only JavaScript Output
The strongest reason to remove accents from a JavaScript string is that some downstream system is not Unicode-aware. Slug generators are the obvious case: a URL path segment cannot contain raw é, ñ or ü without percent-encoding, and percent-encoded Polish text in a URL is hostile to both users and crawlers. The Text To Slug tool applies a separate, explicit ASCII-or-Unicode policy on top of accent handling so the slug rules and the accent rules stay independently auditable. Deduplication is the second case: two contacts named "Müller" and "Muller" are the same person for most mailing workflows, but only after the diacritic on the ü has been folded. Search matching is the third: a Postgres or Elasticsearch index built without ICU collators will treat "São Paulo" and "Sao Paulo" as distinct strings, and accent removal at ingest time keeps the index consistent. ASCII-clean input is also a hard requirement for many legacy systems, MD5 checksums of identifiers, DNS labels and older Windows code pages, and in each of those cases the question is not whether to fold but whether to fold only true diacritics or to also re-spell the transliteration cases.
The contract behind the Remove Accents from Text tool draws that line on purpose. Diacritic removal is on by default and not user-controlled, because the answer is unambiguous for the Latin letters it covers. Transliterations are behind a toggle, because turning ß into ss is a spelling change that the user should consciously make. Stripping non-Latin scripts is behind a toggle for the same reason: it is destructive, and the default is to leave other languages alone. Everything runs inside the browser, nothing is uploaded, and there is no account, which makes the page safe to drop into a JavaScript-adjacent workflow where the input is already sensitive.
Related reading: How to Remove Duplicate Lines from Rhino Text Exports.
Related reading: Remove Duplicate Words in Excel Without Formulas.