graphemely

How to reverse a string with emoji in JavaScript

The classic str.split('').reverse().join('') turns every emoji into broken halves. Reverse by grapheme cluster instead with reverse(str) from graphemely.

The fix, copy and paste

example.js
import { reverse } from '@kwy404/graphemely';

reverse('hello');                     // 'olleh'
reverse('cafe\u{301}');               // 'e\u{301}fac', the accent stays on the e
reverse('a\u{1F1E7}\u{1F1F7}b');      // 'b\u{1F1E7}\u{1F1F7}a', the flag of Brazil survives

Emoji in the code samples are written as JavaScript escapes, for example \u{1F468} for a man. In your own code you can type the emoji directly.

Why the usual one liners break

Broken: code units
example.js
'a\u{1F600}b'.split('').reverse().join('');
// 'b\uDE00\uD83Da'
// two lone surrogates in the wrong order
Still broken: code points
example.js
[...'a\u{1F1E7}\u{1F1F7}b'].reverse().join('');
// 'b\u{1F1F7}\u{1F1E7}a'
// the regional indicators swap: BR becomes RB

The spread version keeps surrogate pairs together, so it looks right for simple emoji. It still reverses the inside of multi code point sequences: flags turn into different flags or into two letters in boxes, a skin tone modifier moves in front of its emoji, a combining accent jumps to the previous letter, and family emoji fall apart into single people.

Catch it with ESLint

The graphemely ESLint plugin reports these patterns with the no-unsafe-reverse rule:

eslint.config.js
import graphemely from '@kwy404/graphemely/eslint-plugin';

export default [graphemely.configs.recommended];

Install

Terminal
npm install @kwy404/graphemely

Works in Node 16 and newer, all modern browsers, Deno and Bun. ESM and CommonJS, TypeScript types included, zero dependencies, about 2 kB gzipped. Try your own text in the playground.

FAQ

split('') splits a string into UTF-16 code units. Reversing them puts the low surrogate before the high surrogate, which leaves two lone surrogates that render as broken boxes.

Only for text without sequences. It keeps code points together but reverses the parts of flags, skin tones, keycaps, ZWJ emoji and combining accents.

Split it into grapheme clusters and reverse those: reverse(str) from graphemely does exactly that.

Related guides