"👩‍💻🎉".length = 7 ??? How to count emojis with Javascript
Tags: tutorial, javascript, emojis
Published: 2022-11-14 09:27:58

---
title: "👩‍💻🎉".length = 7 ??? How to count emojis with Javascript
published: true
description: Emojis are great, but they can be a pain to work with in JavaScript. The problem is that each emoji is actually made up of multiple UTF-16 code units, so when you try to count the number of emojis in a string, you end up with an incorrect count. I'll explain why the count is wrong, and show you how to get the right number of emoji in a JavaScript string.
tags: #tutorial #javascript #emojis
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/scx9psa7o9nco1tey2z5.png
canonical_url: https://cestoliv.com/blog/how-to-count-emojis-with-javascript/
# Use a ratio of 100:42 for best results.
# published_at: 2022-11-13 14:32 +0000
---
*This post was originally published on my blog: [cestoliv.com](https://cestoliv.com)*

## In this article

1. [The problem](#1-the-problem)
2. [A first solution with the spread operator](#2-a-first-solution-with-the-spread-operator)
3. [A second algorithm with Zero Width Joiner](#3-a-second-algorithm-with-zero-width-joiner)
4. [The best solution (to use in production)](#4-the-best-solution-to-use-in-production)
	1. [Use it on Firefox/IE](#41-use-it-on-firefoxie)
	2. [Use it with TypeScript](#42-use-it-with-typescript)

## 1. The problem
This week, a friend of mine encountered a Javascript problem when he wanted to check that his user was entering only one character in a text input.
Indeed, the first solution we think of is to look at the length of the string, but problems occur when this string contains emojis:

```javascript
"a".length // => 1
"🛏".length // => 2 ??
```

![Houston, we have a problem meme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kzefc4jwn21bk19gk5wq.jpg)

In fact, it is quite logical, knowing that the `.length` function in Javascript returns the length of the string in UTF-16 code units, **not the number of visible characters**.

## 2. A first solution with the spread operator

The first solution I thought of was to split the string on each character and then get the number of elements:

```javascript
"🛏".split('') // => ["�","�"]
"🛏".split('').length // => 2
```

**Ouch...** Unfortunately, `.split('')` also splits in UTF-16 code units.

But there is another way to split a string on each character in Javascript, using the spread operator:

```javascript
[..."🛏"] // => ["🛏"]
[..."🛏"].length // => 1, Hooray !!
[..."🛏🎉"] // => ["🛏", "🎉"]
[..."🛏🎉"].length // => 2, Hooray !!

[..."👩‍💻"] // => ["👩"‍, "\u{200D}", "💻"]
[..."👩‍💻"].length // => 3, Oops...
```

**Damn...** Still not, unfortunately for us, some emojis are composed of several emojis, separated by a **"‍" (U+200D, a Zero Width Joiner)**:

```javascript
[..."👩‍💻"] // => ["👩"‍, "\u{200D}", "💻"]

[..."👩‍💻👩‍❤️‍💋‍👩"] // => ["👩", "\u{200D}", "💻", "👩", "\u{200D}", "❤", "\u{fe0f}", "\u{200D}", "💋", "\u{200D}", "👩"]
```

## 3. A second algorithm with Zero Width Joiner

As you can see in this example, to count the number of visible characters, you can count the number of times two characters that are **NOT** Zero Width Joiner are side by side.

For example:
```javascript
[..."a👩‍💻🎉"]
// => ["a", "👩", "\u{200D}", "💻", "🎉"]
//    | 1 |           2           |  3  |
```

So we can make it a simple function:
```javascript
function visibleLength(str) {
	let count = 0;
	let arr = [...str];

	for (c = 0; c < arr.length; c++) {
		if (
			arr[c] != '\u{200D}' &&
			arr[c + 1] != '\u{200D}' &&
			arr[c + 1] != '\u{fe0f}' &&
			arr[c + 1] != '\u{20e3}'
		) {
			count++;
		}
	}
	return count;
}

visibleLength('Hello World'); // => 11
visibleLength('Hello World 👋'); // => 13
visibleLength("I'm going to 🛏 !"); // 16
visibleLength('👨‍👩‍👧‍👦'); // => 1
visibleLength('👩‍💻👩‍❤️‍💋‍👩'); // => 2

visibleLength('🇫🇷'); // => 2 AAAAAAAAAAAAAA!!!
```

Our function works in many cases, **but not in the case of flags**, because flags are two emojis-letters put side by side, but they are not separated by a **Zero Width Joiner**, they are simply transformed into flags by the supported platforms.

```javascript
[..."🇫🇷"] // => ["🇫", "🇷"]
[..."🇺🇸"] // => ["🇺", "🇸"]
```

## 4. The best solution (to use in production)

One of the best solutions we have to handle all these cases is to use a [Grapheme](https://en.wikipedia.org/wiki/Grapheme) algorithm capable of separating strings into visible phrases, words or characters.

To our delight, Javascript integrates this algorithm natively: [Intl.Segmenter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter)

It's pretty easy to use, **AND IT WORKS WITH ALL CHARACTERS!**:

```javascript
function visibleLength(str) {
	return [...new Intl.Segmenter().segment(str)].length
}

visibleLength("I'm going to 🛏 !") // => 16
visibleLength("👩‍💻") // => 1
visibleLength("👩‍💻👩‍❤️‍💋‍👩") // => 2
visibleLength("France 🇫🇷!") // => 9
visibleLength("England 🏴󠁧󠁢󠁥󠁮󠁧󠁿!") // => 10
visibleLength("と日本語の文章") // => 7
```

There is just one small problem, `Intl.Segmenter()` is not compatible at all with Firefox (both Desktop and Mobile) and Internet Explorer.

![Mozilla Developer Network screenshot of Intl Segmenter Browser compatibility](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aan3v2klocuf3kt2xo5n.jpg)

### 4.1 Use it on Firefox/IE

To make this solution compatible with Firefox, we need to use this polyfill: [https://github.com/surferseo/intl-segmenter-polyfill](https://github.com/surferseo/intl-segmenter-polyfill)

Because the file is very large (1.77 MB), we need to make sure that it is only loaded for clients that do not yet support `Intl.Segmenter()`.

Because it's a bit out of the scope of this article, I'm just posting my solution:

{% embed https://replit.com/@cestoliv/JS-Visible-Length %}

### 4.2. Use it with TypeScript

Because this implementation is relatively new, if you want to use `Intl.Segmenter()` with TypeScript, make sure you have at least **ES2022** as target:

```json
// File: tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    // [...]
  }
  // [...]
}
```

**Thanks for reading!**

{% embed https://dev.to/cestoliv %}