# unicode-segmenter

> A lightweight implementation of the Unicode Text Segmentation (UAX #29)

Latest version **0.17.3** (published 2026-07-29) · MIT license · 0 weekly downloads

## Install

```sh
npm install unicode-segmenter
pnpm add unicode-segmenter
yarn add unicode-segmenter
bun add unicode-segmenter
```

## Health

**Score 75/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; has provenance; recently updated; high maintenance score; high quality score.

Warnings: low downloads; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.17.3 |
| Published | 2026-07-29 |
| First published | 2024-04-13 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 0 |
| Unpacked size | 134.8 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 112 |
| Maintainers | cometkim |
| Keywords | unicode, uax29, text-segmentation, grapheme, grapheme-cluster, emoji, intl, polyfill |

## Links

- npm: https://www.npmjs.com/package/unicode-segmenter
- Repository: https://github.com/cometkim/unicode-segmenter
- npm.io page: https://npm.io/package/unicode-segmenter

## Alternatives

- [messageformat](https://npm.io/package/messageformat.md) — 329.7K weekly downloads
- [@mintlify/scraping](https://npm.io/package/@mintlify/scraping.md) — 294.8K weekly downloads
- [@mintlify/previewing](https://npm.io/package/@mintlify/previewing.md) — 209.5K weekly downloads
- [@mintlify/prebuild](https://npm.io/package/@mintlify/prebuild.md) — 209.5K weekly downloads
- [@mintlify/link-rot](https://npm.io/package/@mintlify/link-rot.md) — 206.3K weekly downloads

## Recent versions

- 0.17.3 (latest) — 2026-07-29
- 0.17.2 — 2026-07-26
- 0.17.1 — 2026-07-23
- 0.17.0 — 2026-07-06
- 0.16.0 — 2026-04-23
- 0.15.0 — 2026-01-28
- 0.14.5 — 2025-12-29
- 0.14.4 — 2025-12-14
- 0.14.3 — 2025-12-14
- 0.14.2 — 2025-12-12
- 0.14.1 — 2025-12-05
- 0.14.0 — 2025-08-06
- 0.13.2 — 2025-07-30
- 0.13.1 — 2025-06-20
- 0.13.0 — 2025-05-20
- … 28 more at https://npm.io/package/unicode-segmenter/versions

## README

# unicode-segmenter
[![NPM Package Version](https://img.shields.io/npm/v/unicode-segmenter)](https://npmx.dev/unicode-segmenter)
[![NPM Downloads](https://img.shields.io/npm/dw/unicode-segmenter)](https://npmx.dev/unicode-segmenter)
[![Integration](https://github.com/cometkim/unicode-segmenter/actions/workflows/ci.yml/badge.svg)](https://github.com/cometkim/unicode-segmenter/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/cometkim/unicode-segmenter/graph/badge.svg?token=3rA29JEH4J)](https://codecov.io/gh/cometkim/unicode-segmenter)
[![CodSpeed Badge](https://img.shields.io/endpoint?url=https://codspeed.io/badge.json)](https://codspeed.io/cometkim/unicode-segmenter?utm_source=badge)
[![LICENSE - MIT](https://img.shields.io/github/license/cometkim/unicode-segmenter)](#license)

A lightweight implementation of the [Unicode Text Segmentation (UAX \#29)](https://www.unicode.org/reports/tr29)

- **Spec compliant**: Up-to-date Unicode data, verified by the official Unicode test suites and fuzzed with the native `Intl.Segmenter`, and maintaining 100% test coverage.

- **Excellent compatibility**: It works well on older browsers, edge runtimes, React Native (Hermes) and QuickJS.

- **Zero-dependencies**: It doesn't bloat `node_modules` or the network bandwidth. Like a small minimal snippet.

- **Small bundle size**: It effectively compresses the Unicode data and provides a bundler-friendly format.

- **Extremely efficient**: It's carefully optimized for runtime performance, making it the fastest one in the ecosystem—outperforming even the built-in `Intl.Segmenter`. Its flat lookup tables also keep the memory footprint far smaller than other libraries.

- **TypeScript**: It's fully type-checked, and provides type definitions and JSDoc.

- **ESM-first**: It primarily supports ES modules, and still supports CommonJS.

> [!NOTE]
> unicode-segmenter is now **[e18e] recommendation!**

## Unicode® Version

Unicode® 17.0.0

Unicode® Standard Annex \#29 - [Revision 47](https://www.unicode.org/reports/tr29/tr29-47.html) (2025-08-17)

## APIs

Entries for Unicode text segmentation.

- [`unicode-segmenter/grapheme`](#export-unicode-segmentergrapheme): Segments and counts **extended grapheme clusters**
- [`unicode-segmenter/intl-adapter`](#export-unicode-segmenterintl-adapter): [`Intl.Segmenter`] adapter
- [`unicode-segmenter/intl-polyfill`](#export-unicode-segmenterintl-polyfill): [`Intl.Segmenter`] polyfill

And matchers for extra use cases.

- [`unicode-segmenter/emoji`](#export-unicode-segmenteremoji): Matches single codepoint emojis
- [`unicode-segmenter/general`](#export-unicode-segmentergeneral): Matches single codepoint alphanumerics

### Export `unicode-segmenter/grapheme`

Utilities for text segmentation by extended grapheme cluster rules.

#### Example: Split graphemes

You can split a string into graphemes by simply consuming the `splitGraphemes()` generator.

It yields substrings directly, so it allocates less than `graphemeSegments()`.

```js
import { splitGraphemes } from 'unicode-segmenter/grapheme';

[...splitGraphemes('#️⃣*️⃣0️⃣1️⃣2️⃣')];
// 0: #️⃣
// 1: *️⃣
// 2: 0️⃣
// 3: 1️⃣
// 4: 2️⃣
```

#### Example: Collect graphemes into an array

If you need the result as an array, use `collectGraphemes()` for convenience.

This is a fast version of `[...splitGraphemes(str)]`, 1.5-2.5x faster.

```js
import { collectGraphemes } from 'unicode-segmenter/grapheme';

collectGraphemes('#️⃣*️⃣0️⃣1️⃣2️⃣'); // => ["#️⃣", "*️⃣", "0️⃣", "1️⃣", "2️⃣"]
```

However, it collects all grapheme clusters at once, so it's not good for large text or streamed input.

#### Example: Count graphemes

If you need only the count, use `countGraphemes()`, which is optimized to avoid allocations for segments.

Mostly 2-4x faster than the full segmenter and more GC friendly, so you can call it in a hot path.

```js
import { countGraphemes } from 'unicode-segmenter/grapheme';

'👋 안녕!'.length;
// => 6
countGraphemes('👋 안녕!');
// => 5

'a̐éö̲'.length;
// => 7
countGraphemes('a̐éö̲');
// => 3
```

#### Example: Get full segment info

You can retrieve all grapheme segments using the `graphemeSegments()` generator.

Each segment is returned as a `GraphemeSegmentOutput` object containing the `segment` (the substring), the starting `index`, and the full `input` string. This pattern is similar to the [`Intl.Segmenter`] API, so it can be a drop-in replacement.

```js
import { graphemeSegments } from 'unicode-segmenter/grapheme';

[...graphemeSegments('a̐éö̲\r\n')];
// 0: { segment: 'a̐', index: 0, input: 'a̐éö̲\r\n' }
// 1: { segment: 'é', index: 2, input: 'a̐éö̲\r\n' }
// 2: { segment: 'ö̲', index: 4, input: 'a̐éö̲\r\n' }
// 3: { segment: '\r\n', index: 7, input: 'a̐éö̲\r\n' }
```

#### Example: Build an advanced grapheme matcher

`graphemeSegments()` exposes some knowledge identified in the middle of the process to support some useful cases.

For example, knowing the [Grapheme_Cluster_Break](https://www.unicode.org/reports/tr29/tr29-43.html#Default_Grapheme_Cluster_Table) category at the beginning and end of a segment can help approximately infer the applied boundary rule.

```js
import { graphemeSegments, GraphemeCategory } from 'unicode-segmenter/grapheme';

function* matchEmoji(str) {
  for (const { segment, _catBegin } of graphemeSegments(input)) {
    // `_catBegin` identified as Extended_Pictographic means the segment is emoji
    if (_catBegin === GraphemeCategory.Extended_Pictographic) {
      yield segment;
    }
  }
}

[...matchEmoji('1🌷2🎁3💩4😜5👍')]
// 0: 🌷
// 1: 🎁
// 2: 💩
// 3: 😜
// 4: 👍
```

Or build even more advanced one like an Unicode-aware [TTY string width](https://github.com/cometkim/unicode-string-width) utility.

### Export `unicode-segmenter/intl-adapter`

[`Intl.Segmenter`] API adapter (only `granularity: "grapheme"` available yet)

```js
import { Segmenter } from 'unicode-segmenter/intl-adapter';

// Same API with the `Intl.Segmenter`
const segmenter = new Segmenter();
```

### Export `unicode-segmenter/intl-polyfill`

[`Intl.Segmenter`] API polyfill (only `granularity: "grapheme"` available yet)

```js
// Apply polyfill to the `globalThis.Intl` object.
import 'unicode-segmenter/intl-polyfill';

const segmenter = new Intl.Segmenter();
```

### Export `unicode-segmenter/emoji`

Utilities for matching emoji-like characters.

#### Example: Use Unicode emoji property matches

```js
import {
  isEmojiPresentation,    // match \p{Emoji_Presentation}
  isExtendedPictographic, // match \p{Extended_Pictographic}
} from 'unicode-segmenter/emoji';

isEmojiPresentation('😍'.codePointAt(0));
// => true
isEmojiPresentation('♡'.codePointAt(0));
// => false

isExtendedPictographic('😍'.codePointAt(0));
// => true
isExtendedPictographic('♡'.codePointAt(0));
// => true
```

### Export `unicode-segmenter/general`

Utilities for matching alphanumeric characters.

#### Example: Use Unicode general property matchers

```js
import {
  isLetter,       // match \p{L}
  isNumeric,      // match \p{N}
  isAlphabetic,   // match \p{Alphabetic}
  isAlphanumeric, // match [\p{N}\p{Alphabetic}]
} from 'unicode-segmenter/general';
```

## Runtime Compatibility

`unicode-segmenter` uses only fundamental features of ES2015, making it compatible with most browsers.

To ensure compatibility, the runtime should support:
- [`String.prototype.codePointAt()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt)
- [Typed arrays](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Typed_arrays)
- [Generators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator)
- [Modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)

If the runtime doesn't support these features, it can easily be fulfilled with tools like Babel.

## React Native Support

Since [Hermes doesn't support the `Intl.Segmenter` API](https://github.com/facebook/hermes/blob/main/doc/IntlAPIs.md) yet, `unicode-segmenter` is a good alternative.

`unicode-segmenter` is compiled into small & efficient Hermes bytecode than other JavaScript libraries. See the [benchmark](#hermes-bytecode-stats) for details.

## Comparison

`unicode-segmenter` aims to be lighter and faster than alternatives in the ecosystem while fully spec compliant. So the benchmark is tracking several libraries' performance, bundle size, and Unicode version compliance.

### `unicode-segmenter/grapheme` vs

- [graphemer]@1.4.0
- [grapheme-splitter]@1.0.4
- [@formatjs/intl-segmenter]@12.1.0
- WebAssembly build of [unicode-segmentation]@1.12.0 with minimum bindings
- Built-in [`Intl.Segmenter`] API

#### JS Bundle Stats

| Name                                 | Unicode® | ESM? |    Size | Size (min) | Size (min+gzip) | Size (min+br) | Size (min+zstd) |
|--------------------------------------|----------|------|--------:|-----------:|----------------:|--------------:|----------------:|
| `unicode-segmenter/grapheme`         | 17.0.0   | ✔️   |   7,767 |      4,876 |           2,325 |         2,104 |           2,374 |
| `unicode-segmenter/grapheme` (full*) | 17.0.0   | ✔️   |  10,173 |      5,725 |           2,607 |         2,322 |           2,668 |
| `graphemer`                          | 15.0.0   | ✖️   | 410,435 |     95,104 |          15,752 |        10,660 |          15,911 |
| `grapheme-splitter`                  | 10.0.0   | ✖️   | 122,254 |     23,682 |           7,852 |         4,802 |           6,753 |
| `@formatjs/intl-segmenter`*          | 17.0.0   | ✖️   | 268,301 |    176,759 |          45,988 |        31,701 |          45,370 |
| `unicode-segmentation`*              | 15.1.0   | -    |  56,529 |     52,439 |          24,108 |        17,343 |          24,375 |
| `Intl.Segmenter`*                    | -        | -    |       0 |          0 |               0 |             0 |               0 |

* `unicode-segmenter/grapheme` provides count/split-only API which has much less overhead than full `Segmenter` implementation, which includes some redundant codes but tree-shakable.
* `@formatjs/intl-segmenter` handles grapheme, word, and sentence, but it's not tree-shakable.
* `unicode-segmentation` size contains only minimum WASM binary and its bindings to execute benchmarking. It will increases to expose more features.
* `Intl.Segmenter`'s Unicode data depends on the host, and may not be up-to-date.
* `Intl.Segmenter` may not be available in [some old browsers](https://caniuse.com/mdn-javascript_builtins_intl_segmenter), edge runtimes, or embedded environments.

#### Hermes Bytecode Stats

| Name                                | Bytecode size | Bytecode size (gzip)* |
|-------------------------------------|--------------:|----------------------:|
| `unicode-segmenter/grapheme`        |        15,794 |                 8,916 |
| `unicode-segmenter/grapheme` (full) |        16,003 |                 9,073 |
| `graphemer`                         |       134,085 |                31,770 |
| `grapheme-splitter`                 |        63,942 |                19,165 |
| `@formatjs/intl-segmenter`          |       329,547 |               136,751 |

* The installation size contains _compressed_ assets.

#### Memory Stats

Retained memory per library after segmenting the benchmark corpus, measured by `yarn memory-stats:grapheme` in isolated Node.js processes (median of 5, GC-stabilized deltas).


| Name                         | JS heap | ArrayBuffers* |
|------------------------------|--------:|--------------:|
| `unicode-segmenter/grapheme` | 217 kB  |       19.1 kB |
| `graphemer`                  | 2.32 MB |           0 B |
| `grapheme-splitter`          | 570 kB  |           0 B |
| `@formatjs/intl-segmenter`   | 1.95 MB |           0 B |
| `unicode-segmentation`*      | 214 kB  |             - |
| `Intl.Segmenter`*            | 86.1 kB |             - |

* "ArrayBuffers" is `process.memoryUsage().arrayBuffers`: `unicode-segmenter` keeps its lookup tables in typed arrays, so they live there instead of the JS heap. No other JavaScript library here allocates any.
* `unicode-segmentation`'s WASM linear memory and `Intl.Segmenter`'s ICU data are allocated by native code and invisible to both columns.

#### Runtime Performance

Here is a brief explanation, and you can see [archived benchmark results](benchmark/grapheme/_records).

**Performance in Node.js/Bun/Deno**: `unicode-segmenter/grapheme` has best-in-class performance.
- 5\~48x faster than other JavaScript libraries.
- 3.5\~8x faster than WASM binding of the Rust's [unicode-segmentation].
- 1.3\~2.5x faster than built-in [`Intl.Segmenter`].

**Performance in Browsers**: The performance in browser environments varies greatly due to differences in browser engines, which makes benchmarking inconsistent, but:
- Still significantly faster than other JavaScript libraries.
- Generally outperforms the built-in in the most browser environments, except the Firefox.

**Performance in React Native**: `unicode-segmenter/grapheme` is still faster than alternatives when compiled to Hermes bytecode. It's \~4x faster than `graphemer` and \~34x faster than `grapheme-splitter`.

**Performance in QuickJS**: `unicode-segmenter/grapheme` is the only usable library in terms of performance.

Instead of trusting these claims, you can try `yarn perf:grapheme` directly in your environment or build your own benchmark.

## Acknowledgments

- **The Rust Unicode team ([@unicode-rs](https://github.com/unicode-rs))**:\
   The initial implementation was ported manually from [unicode-segmentation] library.

- **Marijn Haverbeke ([@marijnh](https://github.com/marijnh))**:\
   Inspired a technique that can greatly compress Unicode data table from [his library](https://github.com/marijnh/find-cluster-break).

## LICENSE

[MIT](LICENSE)

[e18e]: https://e18e.dev/
[Hermes]: https://hermesengine.dev/
[QuickJS]: https://bellard.org/quickjs/
[unicode-segmentation]: https://github.com/unicode-rs/unicode-segmentation
[`Intl.Segmenter`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter
[graphemer]: https://github.com/flmnt/graphemer
[grapheme-splitter]: https://github.com/orling/grapheme-splitter
[emoji-regex]: https://github.com/mathiasbynens/emoji-regex
[emojibase-regex]: https://emojibase.dev/docs/regex
[XRegExp]: https://xregexp.com/
[@formatjs/intl-segmenter]: https://formatjs.github.io/docs/polyfills/intl-segmenter/

---
_Source: https://npm.io/package/unicode-segmenter · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
