const family = "👨👩👧";
console.log(family.length);
console.log([...family].length);
const seg = new Intl.Segmenter("en", { granularity: "grapheme" });
console.log([...seg.segment(family)].length);
8, 5 and 1The family emoji is three person emoji joined by two invisible ZWJ (zero width joiner) characters.
• length counts UTF-16 code units. Each person emoji takes 2, and each ZWJ takes 1, so the total is 8.
• Spreading a string splits it into code points: 3 emoji and 2 joiners, so 5.
• Intl.Segmenter with "grapheme" counts what a user sees as one character: 1.
"3, 3 and 1" is the tempting wrong answer, because you see three people. For a character counter, a "max 280 characters" check or cutting text, use Intl.Segmenter. Otherwise you can cut an emoji or an accented letter in half. With granularity: "word", it also splits words in languages without spaces, like Japanese or Thai.