Character Reverser
Reverse a string one character at a time, with the Unicode handling most reversers get wrong. Emoji, flags, skin tones and accented letters move as single units instead of being torn apart.
If you are testing a reverse function, debugging mojibake, or just need a reference implementation you can paste into, this page shows exactly what correct reversal produces β and which of the three plausible definitions of "character" it uses.
Working with words rather than characters? Reverse letters per word
Character Reversal Test Cases
These double as a test suite. If your own reverse function disagrees with any row here, it is almost certainly splitting UTF-16 units or bare code points instead of grapheme clusters.
| Input | Output | What it shows |
|---|---|---|
| abcdef | fedcba | Baseline: pure ASCII reverses trivially. |
| abπcd | dcπba | The emoji stays whole. A naive split("") produces two broken glyphs here. |
| πΊπΈπ¬π§ | π¬π§πΊπΈ | Flags are regional indicator pairs. Reversing the code points would spell two different countries. |
| π¨βπ©βπ§ | π¨βπ©βπ§ | A ZWJ family emoji moves as one character rather than splitting into its members. |
| πππππ ππ | πππ ππππ | Astral mathematical characters are each one code point and survive. |
| cafΓ© | Γ©fac | A precomposed Γ© reverses cleanly as a single character. |
| a b c | c b a | Tabs and newlines are ordinary characters and reverse in place. |
| padded | deddap | Leading and trailing spaces swap ends rather than being trimmed. |
| 1234567890 | 0987654321 | Digit strings reverse without any numeric interpretation. |
| <div class="x"> | >"x"=ssalc vid< | Angle brackets and quotes are not escaped or altered. |
| ABCdef123!@# | #@!321fedCBA | Mixed classes confirm nothing is reordered by type. |
Code Points, Units and Graphemes
There are three plausible answers to "what is a character", and picking the wrong one is why so many reverse functions corrupt text.
UTF-16 code units: the wrong choice
JavaScript strings are sequences of 16-bit units. text.split("").reverse().join("") reverses those units, which swaps the halves of any character outside the Basic Multilingual Plane. Every emoji in the string becomes two replacement glyphs.
Code points: better, but still not a character
Array.from(text) iterates code points, so a surrogate pair is yielded as one two-unit string. That fixes the single-emoji case and is where most reversers stop β but a flag is two code points, a skin tone is two, a family emoji is five, and a decomposed accent is two. Reversing at this level pulls all of them apart.
Grapheme clusters: what this tool uses
A user-perceived character can span several code points. This tool segments with Intl.Segmenter under UAX #29 and reverses those clusters, so πΊπΈ does not become πΈπΊ, ππ½ keeps its skin tone, and an accent stays on its own letter. Browsers without Intl.Segmenter fall back to an equivalent segmenter tested against it.
Why the distinction shows up in counts
The character counts on this page count grapheme clusters, so "ππ" counts as 2 rather than the 4 that String.length reports, and a family emoji counts as 1 rather than 5. If your own counter disagrees with the tool, that is usually why.
Reversal Granularity Compared
Four levels at which a string can be reversed, applied to the same input. Choosing the granularity is the whole decision.
Shared input: abc def ghi jkl
| Transformation | Result | Reach for it when |
|---|---|---|
| Characters | lkj ihg fed cba | You want a true end-to-start string reversal. |
| Characters within words | cba fed ihg lkj | You want per-token reversal with layout preserved. |
| Words | jkl ghi def abc | You want token order flipped, tokens intact. |
| Lines | ghi jkl abc def | You want record order flipped in a list or log. |
Documented Reversal Behaviour
Each of these is covered by a unit test in the repository, so the behaviour is deliberate and will not drift.
Surrogate pairs stay intact
Reversing "abπcd" produces "dcπba". The emoji is a single code point stored as two UTF-16 units, and it is never split. This is asserted directly in the test suite.
ZWJ sequences and flags stay assembled
The family emoji π¨βπ©βπ§ is three emoji plus two zero-width joiners, and πΊπΈ is two regional indicators. Both are single grapheme clusters here, so the family is not scattered into its members and the flag does not come back as πΈπΊ.
Combining marks stay on their base
The decomposed form of Γ© is "e" followed by U+0301. The pair is one cluster, so it moves together and "cafΓ©" reverses to "Γ©fac" in both the precomposed and decomposed forms.
CRLF stays CRLF
A Windows line ending is two characters but one grapheme cluster, so reversing a document with CRLF endings does not leave the line feed in front of the carriage return. There is no need to normalise line endings first.
Notes for Developers
The behaviour above maps onto the equivalent operations in the languages people most often ask about.
JavaScript and TypeScript
Segment with new Intl.Segmenter(locale, { granularity: "grapheme" }) and reverse the segments β that is what this tool does. Array.from(str).reverse().join("") is the cheap approximation: it fixes surrogate pairs but still breaks flags, skin tones and combining marks. Avoid str.split("") entirely.
Python
str[::-1] reverses by code point already, because Python 3 strings are sequences of code points rather than UTF-16 units. Combining marks and emoji sequences still come apart, so use a grapheme library such as regex or grapheme for the same result you see here.
Java and C#
Both use UTF-16 internally, so StringBuilder.reverse() and a naive char[] reversal have the same surrogate problem as JavaScript. Java's StringBuilder.reverse() does special-case surrogate pairs; a hand-rolled loop usually does not.
Go and Rust
Strings are UTF-8 byte sequences. Reversing bytes corrupts any multi-byte character, so iterate runes in Go or chars in Rust and reverse that sequence instead.
When You Need Character-Level Reversal
Testing your own implementation
Paste the emoji and accent rows from the table above into your function. Any difference from the outputs here points straight at a UTF-16 assumption in your code.
Building test fixtures
Reversed strings with mixed scripts make good fixtures for text pipelines, because they exercise encoding paths that plain ASCII never touches.
Interview practice
"Reverse a string" is a classic interview question, and the Unicode follow-up is the part that separates answers. The cases here are the ones worth being able to discuss.
Debugging mojibake
If reversed text renders as replacement characters somewhere in your stack, comparing against this output isolates whether the corruption happened at reversal or later.
Frequently Asked Questions
Paste it into the box above. The tool iterates grapheme clusters rather than UTF-16 units, so the reversal is correct for emoji, flags, accented letters and scripts that use combining marks.
Usually because it uses split("") or indexes the string by position; both operate on UTF-16 units and cut a surrogate pair in half. Array.from fixes that but not multi-code-point emoji β for those you need Intl.Segmenter with grapheme granularity.
A code point is one Unicode scalar value. A user-perceived character β a grapheme cluster β may be several code points, such as a letter plus a combining accent, or emoji joined by zero-width joiners.
Yes. Segmentation follows UAX #29 via Intl.Segmenter, so combining marks, ZWJ sequences, flag pairs, skin-tone modifiers and keycaps all move as single characters. Each case is covered by a unit test rather than left to chance.
By grapheme cluster β what a reader would count by hand. "ππ" counts as two characters here, whereas JavaScript's String.length would report four, and a family emoji counts as one rather than five.
No. The same characters come out, in the opposite order. Nothing is normalised, escaped or re-encoded on the way through.
Yes. Reversal is a single pass and stays fast well past 100,000 characters. A performance test in the repository asserts that all six transformations complete on a 120,000-character document within half a second.
No. Everything runs in your browser, which also means you can use it on code and data you are not allowed to paste into a hosted service.
Related Text Tools
Text Reverser
Compare six backwards-text results side by side from one input.
Backwards Text Generator
Turn any sentence into backwards text, character by character.
Text Inverter
Invert text three ways β backwards, upside down or mirrored β and compare.
Write Backwards Tool
Type forwards and read backwards, plus how to write in reverse by hand.
Sentence Reverser
Reverse a whole sentence or message, by word order or letter by letter.
Letter Reverser
Reverse the letters inside every word while the words stay in place.
Backwards Text Translator
Translate text to backwards and decode backwards text in one pass.
Reverse Words Tool
Reverse word order while every word stays readable.