Skip to main content
TextReverse

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.

abcπŸ˜€deffedπŸ˜€cba

Working with words rather than characters? Reverse letters per word

Try an example
0 characters0 words0 lines
Output
Your transformed text will appear here...
0 characters0 words0 lines
Ctrl + Enter Copy resultEsc Clear

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.

InputOutputWhat it shows
abcdeffedcbaBaseline: pure ASCII reverses trivially.
abπŸ˜€cddcπŸ˜€baThe 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ééfacA precomposed é reverses cleanly as a single character.
a b cc b aTabs and newlines are ordinary characters and reverse in place.
padded deddap Leading and trailing spaces swap ends rather than being trimmed.
12345678900987654321Digit strings reverse without any numeric interpretation.
<div class="x">>"x"=ssalc vid<Angle brackets and quotes are not escaped or altered.
ABCdef123!@##@!321fedCBAMixed 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

TransformationResultReach for it when
Characterslkj ihg fed cbaYou want a true end-to-start string reversal.
Characters within wordscba fed ihg lkjYou want per-token reversal with layout preserved.
Wordsjkl ghi def abcYou want token order flipped, tokens intact.
Linesghi jkl abc defYou 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

Related Text Tools

Browse all text tools β†’