[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"article-intl-segmenter-grapheme-word-sentence":44,"search-suggestions":269,"related-intl-segmenter-grapheme-word-sentence":306,"code:js:true:1qe1twe":437,"code:js:true:14lrr8":438,"code:js:true:1g8dtxu":439,"code:js:true:emyej0":440,"code:js:true:1ogencp":441,"code:js:true:rp916p":442,"code:js:true:1o82r4u":443,"comments-01a02839-480f-7428-914a-2c0442d00855":444},[4,20,32],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Read it. Run it. Prove it.","A post a day on modern web development — most with an editable playground and a quiz that explains every answer. Free, no account needed.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Grammar explained the way good documentation explains an API — one idea at a time, each with a quiz.","amber-400","⌘",[13,15,29],"es",{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":17,"packages":18,"community":17,"certificates":17,"teams":18,"commerce":18},2,{"id":33,"slug":34,"name":35,"tagline":36,"description":37,"accentFrom":38,"accentTo":39,"icon":40,"defaultLocale":13,"locales":41,"features":42,"position":43},"7b3c16f2-931d-410e-802e-e1fa4edab7de","soft","Soft Skills","The half of the job nobody wrote documentation for.","Weekly, on the parts of working life that decide more than your code does — first weeks, meetings, interviews, promotions, and the people around you. Written from what actually happens, and recorded as a podcast you can listen to on the walk.","emerald-400","teal-300","◉",[13],{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":18},3,{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":53,"likeCount":19,"commentCount":19,"author":54,"vertical":59,"topic":60,"tags":63,"_count":75,"playground":77,"body":79,"bodyMd":251,"seo":252,"translationGroupId":256,"series":48,"verticalId":5,"thread":257,"assessments":259,"translations":265,"quiz":267},"01a02839-480f-7428-914a-2c0442d00855","intl-segmenter-grapheme-word-sentence","You're truncating bios with `.slice()`. `Intl.Segmenter` knows where the emoji actually end.",null,"A 30-character bio limit that cuts off mid-emoji isn't a rendering bug — it's .length counting UTF-16 code units instead of what's on screen. Intl.Segmenter counts graphemes, words, and sentences the way a reader actually sees them, and every major browser supports it now.","\u002Fmedia\u002Fcovers\u002Fintl-segmenter-grapheme-word-sentence.png",5,"2026-08-27T17:30:57.707Z",40,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},"javascript","JavaScript",[64,66,69,72],{"slug":61,"name":65,"color":48},"Javascript",{"slug":67,"name":68,"color":48},"webdev","Webdev",{"slug":70,"name":71,"color":48},"frontend","Frontend",{"slug":73,"name":74,"color":48},"tutorial","Tutorial",{"assessments":76},1,{"slug":46,"title":78},"Intl.Segmenter — interactive playground",{"blocks":80,"version":76},[81,85,88,91,96,99,105,108,111,115,118,121,124,128,131,135,138,141,144,148,151,154,158,161,164,167,170,173,176,179,182,186,189,192,195,198,206,209,212,215,218,221,224,227,230,233,236,239,242,245],{"id":82,"html":83,"type":84},"b1","\u003Cp>Somebody fills in their bio: &quot;I love ☕ and my family 👨‍👩‍👧‍👦!&quot; Your limit is 30 characters. You call \u003Ccode>.slice(0, 30)\u003C\u002Fcode> before saving, same as you always do.\u003C\u002Fp>","paragraph",{"id":86,"html":87,"type":84},"b2","\u003Cp>What gets saved is \u003Ccode>&quot;I love ☕ and my family 👨‍👩&quot;\u003C\u002Fcode> — followed by a dangling zero-width joiner with nothing after it. The family of four just lost two kids. On some renderers you get a lone half-emoji instead: a little black diamond with a question mark where a face should be.\u003C\u002Fp>",{"id":89,"html":90,"type":84},"b3","\u003Cp>Nobody touched your CSS. The bug is \u003Ccode>.length\u003C\u002Fcode> — and it&#39;s lying to you about what a &quot;character&quot; is.\u003C\u002Fp>",{"id":92,"html":93,"text":94,"type":95,"level":31},"b4","What \u003Ccode>.length\u003C\u002Fcode> is actually counting","What .length is actually counting","heading",{"id":97,"html":98,"type":84},"b5","\u003Cp>Open a console and check what one emoji costs:\u003C\u002Fp>",{"id":100,"code":101,"type":102,"language":103,"highlight":104},"b6","\"😀\".length          \u002F\u002F 2\n[...\"😀\"].length      \u002F\u002F 1","code","js",[],{"id":106,"html":107,"type":84},"b7","\u003Cp>\u003Ccode>.length\u003C\u002Fcode> isn&#39;t counting characters. It&#39;s counting UTF-16 code units — the 16-bit chunks JavaScript strings are actually made of. Most emoji live outside the range a single 16-bit unit can address, so the engine stores them as a \u003Cstrong>surrogate pair\u003C\u002Fstrong>: two code units that only mean something together. \u003Ccode>😀\u003C\u002Fcode> is one character and two units. \u003Ccode>.slice()\u003C\u002Fcode>, \u003Ccode>.substring()\u003C\u002Fcode>, and \u003Ccode>charAt()\u003C\u002Fcode> all work in code units, with no idea that a pair belongs together.\u003C\u002Fp>",{"id":109,"html":110,"type":84},"b8","\u003Cp>Spreading the string looks like the fix, because it iterates by \u003Cem>code point\u003C\u002Fem> instead of code unit:\u003C\u002Fp>",{"id":112,"code":113,"type":102,"language":103,"highlight":114},"b9","[...\"😀\"].length      \u002F\u002F 1  ✓ fixed",[],{"id":116,"html":117,"type":84},"b10","\u003Cp>That patches the surrogate-pair problem. It does not patch the next one.\u003C\u002Fp>",{"id":119,"html":120,"text":120,"type":95,"level":31},"b11","Where the spread trick still breaks",{"id":122,"html":123,"type":84},"b12","\u003Cp>The family emoji isn&#39;t one code point wearing a costume — it&#39;s four separate people emoji glued together with an invisible joiner character (U+200D, zero-width joiner):\u003C\u002Fp>",{"id":125,"code":126,"type":102,"language":103,"highlight":127},"b13","const family = \"👨‍👩‍👧‍👦\";\n\nfamily.length                       \u002F\u002F 11  — code units\n[...family].length                  \u002F\u002F 7   — code points (4 people + 3 joiners)",[],{"id":129,"html":130,"type":84},"b14","\u003Cp>Seven. Not one. \u003Ccode>[...str]\u003C\u002Fcode> split the sequence right back apart into pieces that mean nothing on their own. A national flag has the same issue in miniature — it&#39;s two &quot;regional indicator&quot; letters standing in for a country code, one grapheme made of two code points:\u003C\u002Fp>",{"id":132,"code":133,"type":102,"language":103,"highlight":134},"b15","const flag = \"🇯🇵\";\nflag.length            \u002F\u002F 4\n[...flag].length       \u002F\u002F 2",[],{"id":136,"html":137,"type":84},"b16","\u003Cp>Every naive approach agrees the flag and the family are worth more than &quot;1.&quot; A reader looking at the screen would tell you they&#39;re each one thing.\u003C\u002Fp>",{"id":139,"html":140,"text":140,"type":95,"level":31},"b17","The fix: segment by what a reader sees",{"id":142,"html":143,"type":84},"b18","\u003Cp>\u003Ccode>Intl.Segmenter\u003C\u002Fcode> doesn&#39;t count units or code points. It counts \u003Cstrong>grapheme clusters\u003C\u002Fstrong> — the actual visual units a reader perceives as one character, using the same Unicode rules that render the emoji in the first place:\u003C\u002Fp>",{"id":145,"code":146,"type":102,"language":103,"highlight":147},"b19","const seg = new Intl.Segmenter(\"en\", { granularity: \"grapheme\" });\n\n[...seg.segment(family)].length     \u002F\u002F 1  ✓\n[...seg.segment(flag)].length       \u002F\u002F 1  ✓",[],{"id":149,"html":150,"type":84},"b20","\u003Cp>One. One. That&#39;s what \u003Ccode>.length\u003C\u002Fcode> should have said all along.\u003C\u002Fp>",{"id":152,"html":153,"type":84},"b21","\u003Cp>Truncation gets the same fix — walk graphemes instead of code units, and the cut lands between characters instead of through one:\u003C\u002Fp>",{"id":155,"code":156,"type":102,"language":103,"highlight":157},"b22","function truncate(str, max) {\n  const seg = new Intl.Segmenter(\"en\", { granularity: \"grapheme\" });\n  const graphemes = [...seg.segment(str)].map(s => s.segment);\n  return graphemes.slice(0, max).join(\"\");\n}\n\ntruncate(\"I love ☕ and my family 👨‍👩‍👧‍👦!\", 26)\n\u002F\u002F \"I love ☕ and my family 👨‍👩‍👧‍👦\" — the family survives, intact",[],{"id":159,"html":160,"type":84},"b23","\u003C!-- playground:start -->",{"id":162,"html":163,"text":163,"type":95,"level":31},"b24","🎮 Try it yourself",{"id":165,"html":166,"type":84},"b25","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fintl-segmenter-grapheme-word-sentence\u002Fplayground\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":168,"html":169,"type":84},"b26","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":171,"html":172,"type":84},"b27","\u003C!-- playground:end -->",{"id":174,"html":175,"type":84},"b28","\u003Cp>Paste your own string with an emoji sequence into it, drag the limit down, and watch \u003Ccode>.slice()\u003C\u002Fcode> mangle it while the grapheme-aware version doesn&#39;t.\u003C\u002Fp>",{"id":177,"html":178,"text":178,"type":95,"level":31},"b29","It does more than graphemes",{"id":180,"html":181,"type":84},"b30","\u003Cp>\u003Ccode>granularity\u003C\u002Fcode> also takes \u003Ccode>&quot;word&quot;\u003C\u002Fcode> and \u003Ccode>&quot;sentence&quot;\u003C\u002Fcode>, and both solve real problems \u003Ccode>.split(&quot; &quot;)\u003C\u002Fcode> can&#39;t:\u003C\u002Fp>",{"id":183,"code":184,"type":102,"language":103,"highlight":185},"b31","const words = new Intl.Segmenter(\"en\", { granularity: \"word\" });\n\nfor (const s of words.segment(\"Hello, world!\")) {\n  console.log(s.segment, \"→ wordLike:\", s.isWordLike);\n}\n\u002F\u002F \"Hello\" → wordLike: true\n\u002F\u002F \",\"     → wordLike: false\n\u002F\u002F \" \"     → wordLike: false\n\u002F\u002F \"world\" → wordLike: true\n\u002F\u002F \"!\"     → wordLike: false",[],{"id":187,"html":188,"type":84},"b32","\u003Cp>\u003Ccode>isWordLike\u003C\u002Fcode> is what makes this useful for a live word counter — filter to segments where it&#39;s \u003Ccode>true\u003C\u002Fcode> and punctuation stops inflating the count. And because it&#39;s \u003Ccode>Intl\u003C\u002Fcode>, pass a different locale and the rules change with it: word segmentation for \u003Ccode>&quot;ja&quot;\u003C\u002Fcode> finds boundaries in Japanese text with no spaces at all, something \u003Ccode>.split(&quot; &quot;)\u003C\u002Fcode> can never do regardless of locale.\u003C\u002Fp>",{"id":190,"html":191,"type":84},"b33","\u003Cp>Sentence segmentation exists too, but it&#39;s worth knowing where it&#39;s honest about its limits: it still trips on abbreviations like &quot;Dr.&quot; inside a sentence, splitting where a human wouldn&#39;t. Use it for rough chunking — a &quot;read more&quot; preview, a text-to-speech feed — not as a grammar-perfect sentence parser.\u003C\u002Fp>",{"id":193,"html":194,"text":194,"type":95,"level":31},"b34","Where this actually matters",{"id":196,"html":197,"type":84},"b35","\u003Cp>You don&#39;t need this for a codebase that only ever sees \u003Ccode>&quot;hello world&quot;\u003C\u002Fcode>. You need it the moment user-generated text meets:\u003C\u002Fp>",{"id":199,"type":200,"items":201,"ordered":18},"b36","list",[202,203,204,205],"\u003Cstrong>Character-limit inputs\u003C\u002Fstrong> — bios, tweet-style composers, SMS previews. Any limit enforced with \u003Ccode>.slice()\u003C\u002Fcode> can split a grapheme cluster and hand a broken half-emoji to the render pipeline.","\u003Cstrong>&quot;Read more&quot; truncation\u003C\u002Fstrong> — same bug, more visible, because it&#39;s live on every card in a feed.","\u003Cstrong>Client-side word\u002Fcharacter counters\u003C\u002Fstrong> — \u003Ccode>.split(&quot; &quot;).length\u003C\u002Fcode> overcounts on punctuation and undercounts on languages without spaces.","\u003Cstrong>Cursor movement in custom text widgets\u003C\u002Fstrong> — pressing &quot;left arrow&quot; once should skip one visual character, not land you inside a surrogate pair.",{"id":207,"html":208,"type":84},"b37","\u003Cp>None of these are exotic. They&#39;re the first bug report you get once your app has users outside a narrow slice of scripts and emoji usage.\u003C\u002Fp>",{"id":210,"html":211,"text":211,"type":95,"level":31},"b38","The part that makes this easy to adopt",{"id":213,"html":214,"type":84},"b39","\u003Cp>\u003Ccode>Intl.Segmenter\u003C\u002Fcode> shipped in Chrome and Edge in November 2020, Safari followed about five months later, and Firefox was the last of the three — it landed there in April 2024, which is the point the API officially became Baseline. There&#39;s no polyfill tax, no bundle-size argument against it — every browser your users are actually on has had it for a while now.\u003C\u002Fp>",{"id":216,"html":217,"type":84},"b40","\u003Cp>The fix for that bio field isn&#39;t a new dependency. It&#39;s swapping \u003Ccode>str.slice(0, n)\u003C\u002Fcode> for the grapheme-aware version above, three lines, and it stops being a bug the next person on your team has to rediscover.\u003C\u002Fp>",{"id":219,"html":220,"type":84},"b41","\u003Cp>So — does your character counter know the difference between a code unit and what&#39;s actually on the screen? Go paste an emoji into your own limit field and find out.\u003C\u002Fp>",{"id":222,"html":223,"type":84},"b42","\u003C!-- quiz:start -->",{"id":225,"html":226,"text":226,"type":95,"level":31},"b43","🧠 Test yourself",{"id":228,"html":229,"type":84},"b44","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fintl-segmenter-grapheme-word-sentence\u002Fquiz\">Take the 8-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":231,"html":232,"type":84},"b45","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":234,"html":235,"type":84},"b46","\u003C!-- quiz:end -->",{"id":237,"type":238},"b47","divider",{"id":240,"html":241,"type":84},"b48","\u003Cp>🚀 \u003Cstrong>Want more like this?\u003C\u002Fstrong> Every guide, playground, and quiz lives on \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">bestpractic.org\u003C\u002Fa>\u003C\u002Fstrong> — open it and \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">sign up free\u003C\u002Fa>\u003C\u002Fstrong> so the next one finds you.\u003C\u002Fp>",{"id":243,"html":244,"type":84},"b49","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":246,"type":200,"items":247,"ordered":18},"b50",[248,249,250],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","Somebody fills in their bio: \"I love ☕ and my family 👨‍👩‍👧‍👦!\" Your limit is 30 characters. You call `.slice(0, 30)` before saving, same as you always do.\n\nWhat gets saved is `\"I love ☕ and my family 👨‍👩\"` — followed by a dangling zero-width joiner with nothing after it. The family of four just lost two kids. On some renderers you get a lone half-emoji instead: a little black diamond with a question mark where a face should be.\n\nNobody touched your CSS. The bug is `.length` — and it's lying to you about what a \"character\" is.\n\n## What `.length` is actually counting\n\nOpen a console and check what one emoji costs:\n\n```js\n\"😀\".length          \u002F\u002F 2\n[...\"😀\"].length      \u002F\u002F 1\n```\n\n`.length` isn't counting characters. It's counting UTF-16 code units — the 16-bit chunks JavaScript strings are actually made of. Most emoji live outside the range a single 16-bit unit can address, so the engine stores them as a **surrogate pair**: two code units that only mean something together. `😀` is one character and two units. `.slice()`, `.substring()`, and `charAt()` all work in code units, with no idea that a pair belongs together.\n\nSpreading the string looks like the fix, because it iterates by *code point* instead of code unit:\n\n```js\n[...\"😀\"].length      \u002F\u002F 1  ✓ fixed\n```\n\nThat patches the surrogate-pair problem. It does not patch the next one.\n\n## Where the spread trick still breaks\n\nThe family emoji isn't one code point wearing a costume — it's four separate people emoji glued together with an invisible joiner character (U+200D, zero-width joiner):\n\n```js\nconst family = \"👨‍👩‍👧‍👦\";\n\nfamily.length                       \u002F\u002F 11  — code units\n[...family].length                  \u002F\u002F 7   — code points (4 people + 3 joiners)\n```\n\nSeven. Not one. `[...str]` split the sequence right back apart into pieces that mean nothing on their own. A national flag has the same issue in miniature — it's two \"regional indicator\" letters standing in for a country code, one grapheme made of two code points:\n\n```js\nconst flag = \"🇯🇵\";\nflag.length            \u002F\u002F 4\n[...flag].length       \u002F\u002F 2\n```\n\nEvery naive approach agrees the flag and the family are worth more than \"1.\" A reader looking at the screen would tell you they're each one thing.\n\n## The fix: segment by what a reader sees\n\n`Intl.Segmenter` doesn't count units or code points. It counts **grapheme clusters** — the actual visual units a reader perceives as one character, using the same Unicode rules that render the emoji in the first place:\n\n```js\nconst seg = new Intl.Segmenter(\"en\", { granularity: \"grapheme\" });\n\n[...seg.segment(family)].length     \u002F\u002F 1  ✓\n[...seg.segment(flag)].length       \u002F\u002F 1  ✓\n```\n\nOne. One. That's what `.length` should have said all along.\n\nTruncation gets the same fix — walk graphemes instead of code units, and the cut lands between characters instead of through one:\n\n```js\nfunction truncate(str, max) {\n  const seg = new Intl.Segmenter(\"en\", { granularity: \"grapheme\" });\n  const graphemes = [...seg.segment(str)].map(s => s.segment);\n  return graphemes.slice(0, max).join(\"\");\n}\n\ntruncate(\"I love ☕ and my family 👨‍👩‍👧‍👦!\", 26)\n\u002F\u002F \"I love ☕ and my family 👨‍👩‍👧‍👦\" — the family survives, intact\n```\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fintl-segmenter-grapheme-word-sentence\u002Fplayground)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\nPaste your own string with an emoji sequence into it, drag the limit down, and watch `.slice()` mangle it while the grapheme-aware version doesn't.\n\n## It does more than graphemes\n\n`granularity` also takes `\"word\"` and `\"sentence\"`, and both solve real problems `.split(\" \")` can't:\n\n```js\nconst words = new Intl.Segmenter(\"en\", { granularity: \"word\" });\n\nfor (const s of words.segment(\"Hello, world!\")) {\n  console.log(s.segment, \"→ wordLike:\", s.isWordLike);\n}\n\u002F\u002F \"Hello\" → wordLike: true\n\u002F\u002F \",\"     → wordLike: false\n\u002F\u002F \" \"     → wordLike: false\n\u002F\u002F \"world\" → wordLike: true\n\u002F\u002F \"!\"     → wordLike: false\n```\n\n`isWordLike` is what makes this useful for a live word counter — filter to segments where it's `true` and punctuation stops inflating the count. And because it's `Intl`, pass a different locale and the rules change with it: word segmentation for `\"ja\"` finds boundaries in Japanese text with no spaces at all, something `.split(\" \")` can never do regardless of locale.\n\nSentence segmentation exists too, but it's worth knowing where it's honest about its limits: it still trips on abbreviations like \"Dr.\" inside a sentence, splitting where a human wouldn't. Use it for rough chunking — a \"read more\" preview, a text-to-speech feed — not as a grammar-perfect sentence parser.\n\n## Where this actually matters\n\nYou don't need this for a codebase that only ever sees `\"hello world\"`. You need it the moment user-generated text meets:\n\n- **Character-limit inputs** — bios, tweet-style composers, SMS previews. Any limit enforced with `.slice()` can split a grapheme cluster and hand a broken half-emoji to the render pipeline.\n- **\"Read more\" truncation** — same bug, more visible, because it's live on every card in a feed.\n- **Client-side word\u002Fcharacter counters** — `.split(\" \").length` overcounts on punctuation and undercounts on languages without spaces.\n- **Cursor movement in custom text widgets** — pressing \"left arrow\" once should skip one visual character, not land you inside a surrogate pair.\n\nNone of these are exotic. They're the first bug report you get once your app has users outside a narrow slice of scripts and emoji usage.\n\n## The part that makes this easy to adopt\n\n`Intl.Segmenter` shipped in Chrome and Edge in November 2020, Safari followed about five months later, and Firefox was the last of the three — it landed there in April 2024, which is the point the API officially became Baseline. There's no polyfill tax, no bundle-size argument against it — every browser your users are actually on has had it for a while now.\n\nThe fix for that bio field isn't a new dependency. It's swapping `str.slice(0, n)` for the grapheme-aware version above, three lines, and it stops being a bug the next person on your team has to rediscover.\n\nSo — does your character counter know the difference between a code unit and what's actually on the screen? Go paste an emoji into your own limit field and find out.\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 8-question quiz →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fintl-segmenter-grapheme-word-sentence\u002Fquiz)**\n\n_Instant feedback, a hint on every question, and an explanation for each answer — right or wrong._\n\n\u003C!-- quiz:end -->\n\n---\n\n🚀 **Want more like this?** Every guide, playground, and quiz lives on **[bestpractic.org](https:\u002F\u002Fbestpractic.org\u002F)** — open it and **[sign up free](https:\u002F\u002Fbestpractic.org\u002F)** so the next one finds you.\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)",{"title":253,"canonical":254,"description":255},"You're truncating bios with `.slice()`. `Intl.Segmenter` knows where t","https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fintl-segmenter-grapheme-word-sentence","A 30-character bio limit that cuts off mid-emoji isn't a rendering bug — it's .length counting UTF-16 code units instead of what's on screen. Intl.Segmenter counts graphemes, words","01a02839-480f-7428-914a-32d41eff170e",{"id":258,"locked":18},"01a02839-4838-728a-b56e-2cd55ad9a3c6",[260],{"id":261,"slug":46,"title":262,"_count":263},"01a02839-4855-71cc-8994-5719037e43c3","Intl.Segmenter and string truncation",{"questions":264},8,[266],{"locale":13,"slug":46},{"id":261,"slug":46,"title":262,"_count":268,"questionCount":264},{"questions":264},[270,272,274,276,280,284,288,289,293,296,300,303],{"slug":67,"name":68,"articles":271},69,{"slug":70,"name":71,"articles":273},60,{"slug":61,"name":65,"articles":275},59,{"slug":277,"name":278,"articles":279},"css","Css",26,{"slug":281,"name":282,"articles":283},"typescript","Typescript",10,{"slug":285,"name":286,"articles":287},"performance","Performance",9,{"slug":73,"name":74,"articles":264},{"slug":290,"name":291,"articles":292},"grammar","Grammar",6,{"slug":294,"name":295,"articles":51},"react","React",{"slug":297,"name":298,"articles":299},"html","Html",4,{"slug":301,"name":302,"articles":299},"node","Node",{"slug":304,"name":305,"articles":299},"programming","Programming",{"items":307,"meta":435},[308,320,339,358,376,396,415],{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":309,"likeCount":19,"commentCount":19,"author":310,"vertical":311,"topic":312,"tags":313,"_count":318,"playground":319,"hasQuiz":17,"hasPlayground":17},41,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[314,315,316,317],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":70,"name":71,"color":48},{"slug":73,"name":74,"color":48},{"assessments":76},{"slug":46},{"id":321,"slug":322,"title":323,"subtitle":48,"excerpt":324,"coverUrl":325,"locale":13,"readingMinutes":51,"publishedAt":326,"viewCount":327,"likeCount":19,"commentCount":19,"author":328,"vertical":329,"topic":330,"tags":332,"_count":337,"playground":338,"hasQuiz":17,"hasPlayground":17},"01a02319-4485-7310-b494-dc94bdbfb49c","css-field-sizing-auto-grow-textarea","Delete Your Auto-Resize Textarea JS. CSS Does It Now.","The scrollHeight hack everyone copy-pastes for auto-growing textareas has a one-line CSS replacement — plus the one gotcha that bites if you skip max-height.","\u002Fmedia\u002Fcovers\u002Fcss-field-sizing-auto-grow-textarea.png","2026-08-26T06:56:38.974Z",130,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":277,"name":331},"CSS",[333,334,335,336],{"slug":277,"name":278,"color":48},{"slug":67,"name":68,"color":48},{"slug":70,"name":71,"color":48},{"slug":61,"name":65,"color":48},{"assessments":76},{"slug":322},{"id":340,"slug":341,"title":342,"subtitle":48,"excerpt":343,"coverUrl":344,"locale":13,"readingMinutes":345,"publishedAt":346,"viewCount":347,"likeCount":19,"commentCount":19,"author":348,"vertical":349,"topic":350,"tags":351,"_count":356,"playground":357,"hasQuiz":17,"hasPlayground":17},"019fe661-0c1b-7408-8419-531dae95bdf1","weekly-javascript-closures","JavaScript Closures: The Complete Guide (with Cheat Sheet)","Learn how JavaScript closures really work — lexical scope, private state, memoization, and the classic loop bug — with worked examples and a cheat sheet.","\u002Fmedia\u002Fcovers\u002Fweekly-javascript-closures.png",14,"2026-08-25T08:53:48.904Z",230,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[352,353,354,355],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":73,"name":74,"color":48},{"slug":304,"name":305,"color":48},{"assessments":76},{"slug":341},{"id":359,"slug":360,"title":361,"subtitle":48,"excerpt":362,"coverUrl":363,"locale":13,"readingMinutes":51,"publishedAt":364,"viewCount":365,"likeCount":19,"commentCount":19,"author":366,"vertical":367,"topic":368,"tags":369,"_count":374,"playground":375,"hasQuiz":17,"hasPlayground":17},"01a01df1-d6d5-71e8-bba6-1242e581730b","web-animations-api-imperative-control","CSS Animations Can't Pause. The Web Animations API Can.","Toggling a class to restart a CSS animation works — until you need to pause it, reverse it mid-flight, or ask how far through it is. element.animate() returns an object with those methods built in, natively, in every browser you already support.","\u002Fmedia\u002Fcovers\u002Fweb-animations-api-imperative-control.png","2026-08-25T06:55:40.108Z",147,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[370,371,372,373],{"slug":61,"name":65,"color":48},{"slug":277,"name":278,"color":48},{"slug":67,"name":68,"color":48},{"slug":70,"name":71,"color":48},{"assessments":76},{"slug":360},{"id":377,"slug":378,"title":379,"subtitle":48,"excerpt":380,"coverUrl":381,"locale":13,"readingMinutes":345,"publishedAt":382,"viewCount":383,"likeCount":19,"commentCount":76,"author":384,"vertical":385,"topic":386,"tags":389,"_count":394,"playground":395,"hasQuiz":17,"hasPlayground":17},"01a03410-2db8-735b-88b2-5285459dc915","vue-weekly-reactivity-ref-vs-reactive","Vue Reactivity Explained: ref vs reactive (+ Cheat Sheet)","A complete guide to Vue reactivity: how ref and reactive really work, why destructuring breaks updates, when to use each, and a copy-paste cheat sheet.","\u002Fmedia\u002Fcovers\u002Fvue-weekly-reactivity-ref-vs-reactive.png","2026-08-24T13:57:38.080Z",400,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":387,"name":388},"vue","Vue",[390,391,392,393],{"slug":387,"name":388,"color":48},{"slug":61,"name":65,"color":48},{"slug":73,"name":74,"color":48},{"slug":67,"name":68,"color":48},{"assessments":76},{"slug":378},{"id":397,"slug":398,"title":399,"subtitle":48,"excerpt":400,"coverUrl":401,"locale":13,"readingMinutes":402,"publishedAt":403,"viewCount":404,"likeCount":19,"commentCount":19,"author":405,"vertical":406,"topic":407,"tags":408,"_count":413,"playground":414,"hasQuiz":17,"hasPlayground":17},"019fe660-932f-704d-80f6-f8646b604799","weekly-javascript-proxy-reflect","JavaScript Proxy and Reflect: The Complete Guide","Learn JavaScript Proxy and Reflect from the ground up — traps, invariants, and reactive state — with worked examples and a copy-paste cheat sheet.","\u002Fmedia\u002Fcovers\u002Fweekly-javascript-proxy-reflect.png",15,"2026-08-24T07:06:37.948Z",309,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[409,410,411,412],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":73,"name":74,"color":48},{"slug":304,"name":305,"color":48},{"assessments":76},{"slug":398},{"id":416,"slug":417,"title":418,"subtitle":48,"excerpt":419,"coverUrl":420,"locale":13,"readingMinutes":51,"publishedAt":421,"viewCount":422,"likeCount":19,"commentCount":19,"author":423,"vertical":424,"topic":425,"tags":426,"_count":433,"playground":434,"hasQuiz":17,"hasPlayground":17},"01a013a2-ff42-738e-b6d0-cda8f338a976","import-maps-bare-specifiers-native-modules","One version bump, 40 files to edit. Import maps fix that.","Bare specifiers like import _ from 'lodash' don't work in a browser without a bundler — unless you tell the browser how to resolve them yourself, in one JSON block instead of forty hardcoded URLs.","\u002Fmedia\u002Fcovers\u002Fimport-maps-bare-specifiers-native-modules.png","2026-08-23T06:48:56.911Z",222,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[427,428,429,430],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":70,"name":71,"color":48},{"slug":431,"name":432,"color":48},"browser","Browser",{"assessments":76},{"slug":417},{"page":76,"perPage":436,"total":275,"totalPages":287},7,"\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-str\">\"😀\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>length          \u003Cspan class=\"shj-cmnt\">\u002F\u002F 2\n\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">...\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"😀\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>length      \u003Cspan class=\"shj-cmnt\">\u002F\u002F 1\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-oneline\" data-lang=\"js\">\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">...\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"😀\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>length      \u003Cspan class=\"shj-cmnt\">\u002F\u002F 1  ✓ fixed\u003C\u002Fspan>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> family \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"👨‍👩‍👧‍👦\"\u003C\u002Fspan>;\n\nfamily\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>length                       \u003Cspan class=\"shj-cmnt\">\u002F\u002F 11  — code units\n\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">...\u003C\u002Fspan>family\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>length                  \u003Cspan class=\"shj-cmnt\">\u002F\u002F 7   — code points (4 people + 3 joiners)\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> flag \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"🇯🇵\"\u003C\u002Fspan>;\nflag\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>length            \u003Cspan class=\"shj-cmnt\">\u002F\u002F 4\n\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">...\u003C\u002Fspan>flag\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>length       \u003Cspan class=\"shj-cmnt\">\u002F\u002F 2\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> seg \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Intl\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-class\">Segmenter\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"en\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> granularity\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"grapheme\"\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">...\u003C\u002Fspan>seg\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">segment\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>family\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>length     \u003Cspan class=\"shj-cmnt\">\u002F\u002F 1  ✓\n\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">...\u003C\u002Fspan>seg\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">segment\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>flag\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>length       \u003Cspan class=\"shj-cmnt\">\u002F\u002F 1  ✓\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">truncate\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>str\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> max\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> seg \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Intl\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-class\">Segmenter\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"en\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> granularity\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"grapheme\"\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> graphemes \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">...\u003C\u002Fspan>seg\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">segment\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>str\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">map\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>s \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> s\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>segment\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> graphemes\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">slice\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">0\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> max\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">join\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-func\">truncate\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"I love ☕ and my family 👨‍👩‍👧‍👦!\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-num\">26\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F \"I love ☕ and my family 👨‍👩‍👧‍👦\" — the family survives, intact\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> words \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Intl\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-class\">Segmenter\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"en\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> granularity\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"word\"\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-kwd\">for\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> s \u003Cspan class=\"shj-kwd\">of\u003C\u002Fspan> words\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">segment\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"Hello, world!\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  console\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">log\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>s\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>segment\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"→ wordLike:\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> s\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>isWordLike\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F \"Hello\" → wordLike: true\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F \",\"     → wordLike: false\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F \" \"     → wordLike: false\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F \"world\" → wordLike: true\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F \"!\"     → wordLike: false\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>",{"locked":18,"total":19,"comments":445},[]]