How Lodash Truncate Handles Zero-Width Joiners

When truncating strings containing Zero-Width Joiner (ZWJ) characters (\u200D), the Lodash JavaScript library prevents visual corruption by treating entire emoji and character sequences as single, indivisible grapheme clusters. Rather than relying on native JavaScript string methods that operate on UTF-16 code units, _.truncate relies on specialized internal Unicode detection and segmentation regular expressions. This ensures composite characters—such as complex multi-person emojis or modified glyphs—are measured accurately and are not cut in half during truncation.

The Challenge with Native JavaScript Truncation

In standard JavaScript, strings are indexed by 16-bit code units. Complex Unicode sequences, such as the sequence for the technologist emoji (👨‍💻), consist of multiple code points: the base man emoji (\uD83D\uDC68), a zero-width joiner (\u200D), and a personal computer (\uD83D\uDCBB). If native String.prototype.slice() or .length is used to truncate after a fixed number of units, the slice might land directly on or after the ZWJ character, severing the sequence and rendering broken or unintended glyphs.

Lodash's Unicode Detection

Before slicing, _.truncate determines whether the input string contains complex Unicode structures. It checks the string against an internal regular expression pattern (reHasUnicode).

This regular expression specifically checks for the presence of:

If reHasUnicode.test(string) returns false, Lodash defaults to standard string slicing for performance. If it returns true, Lodash routes the string through a specialized Unicode parsing workflow.

Grapheme Segmentation via unicodeToArray

When a ZWJ is present, Lodash converts the string into an array of visual graphemes using unicodeToArray (or stringToArray). This function matches the string against a comprehensive Unicode regular expression (reUnicode).

Within reUnicode, Lodash explicitly defines patterns that combine base characters, variation selectors, and modifiers with ZWJ characters:

// Conceptual representation of Lodash's ZWJ matching logic
const reOptMod = `(?:${rsModifier})?`;
const reOptVar = `[${rsVarRange}]?`;
const reOptJoin = `(?:${rsZWJ}(?:${[rsNonAstral, rsRegional, rsSurrPair].join('|')})${reOptVar}${reOptMod})*`;

This pattern matches any base symbol and consumes subsequent \u200D characters alongside their associated join targets repeatedly. As a result:

  1. Atomic Grouping: The entire chain (e.g., 👨 + ZWJ + 💻) is matched as a single array element instead of three separate parts.
  2. Length Measurement: Lodash calculates the string's length based on the number of visual symbols, meaning a multi-part ZWJ sequence counts toward the truncation limit as one character.

Truncation and Reassembly

Once split into an array of grapheme clusters, _.truncate extracts the desired number of visual elements using array slicing (Array.prototype.slice). Because the ZWJ sequences are preserved inside individual array elements, the slice cannot sever a joiner from its linked symbols.

Finally, Lodash joins the sliced array back into a string and appends the configured omission string (defaulting to '...'), ensuring the truncated output remains visually coherent and free of broken Unicode artifacts.