The problem
Most text truncation in JavaScript looks like this:
str.slice(0, 100) + "..."
Looks fine until you hit real-world content. This breaks in several ways:
- Emoji — 👨👩👧 is multiple codepoints. Slicing mid-sequence produces garbage characters.
- Accented characters — é can be e + a combining accent codepoint. Naive slicing splits them.
- Word boundaries — you get "Hello, W..." instead of "Hello,..."
- Ellipsis budget — the ... itself takes space that most implementations don't account for.
The solution
trimoji uses the native Intl.Segmenter API — no regex hacks, no dependencies — to truncate strings at proper grapheme, word, or sentence boundaries.
import { trimoji } from "trimoji"
// word boundary (default)
trimoji("Hello 👋, World!", 10)
// ➡️ "Hello 👋,…"
// grapheme boundary
trimoji("Café 🎉 World!", {
maxLength: 7,
boundary: "grapheme"
})
// ➡️ "Café 🎉…"
// sentence boundary
trimoji("Hello, World! How are you?", {
maxLength: 15,
boundary: "sentence"
})
// ➡️ "Hello, World!…"
// custom ellipsis
trimoji("Hello, World!", {
maxLength: 10,
ellipsis: "..."
})
// ➡️ "Hello,..."
// empty ellipsis — hard cut
trimoji("Hello, World!", {
maxLength: 5,
ellipsis: ""
})
// ➡️ "Hello"
Why Intl.Segmenter?
Intl.Segmenter is a native browser API that understands Unicode text segmentation rules. It knows that 👨👩👧 is one visible character, that é (as e + combining accent) is one grapheme, and where word and sentence boundaries actually are across different languages.
It's supported in all modern browsers, Node.js 16+, Bun, and Deno — so no polyfills needed.
Fallback behavior
When the budget is tight, trimoji falls back gracefully:
-
sentence➡️ falls back towordif the first sentence doesn't fit -
word➡️ falls back tographemeif the first word doesn't fit
So you never get an empty string or a crash.
API
trimoji(str: string, options: number | TrimOptions): string
| Option | Type | Default | Description |
|---|---|---|---|
maxLength |
number |
— | Max visible length including ellipsis |
ellipsis |
string |
"…" |
Appended at the cut point |
boundary |
`"grapheme" \ | "word" \ | "sentence"` |
locale |
string |
"en" |
BCP 47 locale for Intl.Segmenter
|
There's also a utility export:
import { graphemeLength } from "trimoji"
graphemeLength("café") // 4
graphemeLength("👨👩👧") // 1
graphemeLength("Hi 👋!") // 5
Install
bun add trimoji
# or
npm install trimoji
4.4 kB packed. Zero dependencies.
Top comments (0)