graphemely

Why substring breaks emoji, and a Unicode safe substring

substring, substr and slice use UTF-16 code unit indices, so any index that falls inside an emoji cuts it. slice(str, start, end) from graphemely uses grapheme indices, so every cut is safe.

The fix, copy and paste

example.js
import { slice, at, splitAt } from '@kwy404/graphemely';

const s = 'I \u{2764}\u{FE0F} JS';
s.slice(0, 3);            // 'I \u{2764}', the emoji presentation selector is lost
slice(s, 0, 3);           // 'I \u{2764}\u{FE0F}'
slice('hello', -3);       // 'llo', negative indices work like String.prototype.slice
at('ab\u{1F600}', -1);    // '\u{1F600}', the whole emoji
splitAt('hello', 2);      // ['he', 'llo']

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.

What goes wrong with code unit indices

Broken
example.js
const s = '\u{1F600}\u{1F600}\u{1F600}';
s.substring(0, 3);
// '\u{1F600}\uD83D', one and a half emoji
s.charAt(0);
// '\uD83D', half of an emoji
Fixed
example.js
import { slice, at } from '@kwy404/graphemely';

slice(s, 0, 2);
// two whole emoji
at(s, 0);
// '\u{1F600}'

Indices often come from somewhere else (a cursor position, a search match, a database limit). If they were computed in code units, convert the logic to graphemes end to end, and use isWellFormed to assert that a result has no lone surrogates.

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

Its indices count UTF-16 code units. An emoji uses two or more code units, so an index can point to the middle of it.

They differ in how they treat negative and swapped arguments, but all three count UTF-16 code units, so all three can break emoji.

Use slice(str, start, end) from graphemely. It has the same contract as String.prototype.slice, including negative indices, but counts grapheme clusters.

Related guides