The dumb reason why flag emojis aren't working on your site in Chrome on Windows

7 min read Original article ↗

So, you thought you’d be fancy and use cute little flag emojis in your UI as extra decoration to go along with country/language options. It’s perfect; it provides an extra visual cue to help users quickly find the country they’re looking for, and it just looks nice.

You can even dynamically derive country flag emojis from region codes with a simple code snippet, super cool!

// The offset to shift the ASCII code of each character in an ISO country code string by
// to derive the equivalent flag emoji for that code.
const EMOJI_CHARACTER_OFFSET = 127397;

const getEmojiForCountryCode = (countryCode: string) =>
  String.fromCodePoint(
    ...countryCode
      .toUpperCase()
      .split('')
      .map((char) => char.charCodeAt(0) + EMOJI_CHARACTER_OFFSET),
  );

// "en-US"
const currentLanguageCode = navigator.language;
// "US"
const currentCountryCode = currentLanguageCode.split("-")[1];
// "🇺🇸"
getEmojiForCountryCode(currentCountryCode);
// "🇫🇷"
getEmojiForCountryCode("FR");
// "🇸🇪"
getEmojiForCountryCode("SE");

Life is good.

A bug report comes in.

Some customers are saying the flag emojis are showing up as weird misaligned letters? I can’t recreate it on my machine.

Huh?

Maybe they’re using an ancient machine or extremely outdated browser which doesn’t support rendering emojis?

No. After gathering more information, we find they’re using the latest version of Chrome on a Windows machine.

What on earth is happening? Out of desperation I asked ChatGPT. It was useless as usual.

After doing more digging than I feel like I should have needed to, I found my answer: it appears that due to concerns about the fact that acknowledging the existence of certain countries can be perceived as a nominally political stance, Microsoft has opted to just avoid the issue altogether by not including country flag emojis in Windows’ system font.

Problem solved! Can you imagine if, *gasp*, your computer could render a Taiwanese 🇹🇼 or Palestinian 🇵🇸 flag? The horror!

This seems like a Big Choice, yet there is very little official documentation acknowledging it, mainly just confused people like me posting on StackOverflow trying to figure out what’s going on.

So, what does Windows do with flag emojis then? It just renders the letters of the country’s ISO code, usually in an extremely ugly and poorly-aligned manner.

A graphic showing how the British flag emoji is displayed on different platforms. It is rendered as an icon of the British flag as expected on Android, macOS/iOS, and Firefox on Windows, but is rendered as just the letters 'GB' on Windows.
Source: Using emoji on the web - Fully Stacked

You may have noted from the image above that Firefox on Windows doesn’t have this issue. That’s because Mozilla stepped up by shipping their own custom flag emojis with Firefox to fill the gap that Windows left.

Why wouldn’t Chrome do the same thing?

… We currently have no plan to ship our own emoji font alongside the browser, hence marking as WontFix, works as intended. Alternatively, the website that you’re visiting may consider providing an emoji font that has coverage for these flags. Google Employee, May 21, 2021

Oh. Okay.

I’m of two minds about Google’s position here: on the one hand, I respect stubbornly refusing to cover for another corporation’s BS which shouldn’t be your problem.

However, obviously Firefox was able to do it without too much trouble, and Chrome on Windows represents a massive userbase who is forced to live with the fallout from this decision.

This means a worse experience for developers who have to account for this garbage, and a worse experience for users because rendering flag emojis consistently on all devices now requires JavaScript to detect flag emoji support and/or downloading image/font assets which they shouldn’t need in the first place.

What you can do about it

If you absolutely need flag emojis, your two options are:

Replace live emojis with hard-coded image/SVG icons

Using hard-coded images is probably appropriate in cases where your needs are pretty simple, but I feel like it would become a huge pain if you’re doing something more dynamic where you won’t know exactly which flag icons you’ll need until runtime. In this case, I could imagine that using a big SVG spritesheet might work decently well, but I can’t find any good existing open source examples of that approach. Adding that to my list of open source projects I could do but probably won’t get around to.

Polyfill the flag emojis with a custom font

Adding a custom polyfill font is interesting. The Twemoji font is popular for this (it’s what Mozilla uses in Firefox), and you can use the unicode-range CSS property on the font face declaration to only use the subset of the font which covers the flag emojis (although you’ll still be on the hook for downloading the entire 1.4MB font file).

@font-face {
  font-family: Twemoji;
  unicode-range: U+1F1E6-1F1FF, U+1F3F4, U+E0062-E0063, U+E0065, U+E0067, U+E006C, U+E006E, U+E0073-E0074, U+E0077, U+E007F;
  src: url("/fonts/twemoji.ttf") format("ttf");
}

:root {
  font-family: Twemoji, "My Main Font";
}

I haven’t used it, but this Country Flag Emoji Polyfill library is taking a pretty nicely balanced approach to the problem. It uses JavaScript to detect whether the browser supports country flag emojis, and will only load in the polyfill font if necessary. It also uses a custom subset of the Twemoji font with only the flag emoji characters, significantly reducing the font download size. I wish their code wasn’t so reliant on hard-coded CDN urls, but at a minimum it seems like a great starting point if you need something like it.

On that note…

How do you programmatically detect country flag emoji support?

It’s dumber than you think: you just draw an emoji on a canvas and use brute force to guess at whether it was rendered correctly.

My approach was to draw an American flag emoji and then look at the pixels to see if any of them contained a non-grayscale color. This works because the Windows fallback emoji is just black/white text, so as long as we test against a flag which isn’t supposed to be grayscale, we should be able to tell pretty easily whether it was rendered correctly or not.

export const getDoesBrowserSupportFlagEmojis = (): boolean => {
  const canvas = document.createElement('canvas');
  canvas.height = 1;
  canvas.width = 1;
  const ctx = canvas.getContext('2d');
  if (!ctx) {
    return false;
  }
  ctx.font = `${canvas.height}px sans-serif`;
  const flagEmoji = '🇺🇸';
  ctx.fillText(flagEmoji, 0, canvas.height);
  // Read the canvas pixels and search for any non-grayscale pixels. We used an american flag
  // emoji so there should be some red or blue in there if the browser rendered the flag emoji correctly.
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
  for (let i = 0; i < imageData.length; i += 4) {
    if (imageData[i + 3] === 0) {
      // Skip transparent pixels
      continue;
    }
    if (imageData[i] !== imageData[i + 1] || imageData[i] !== imageData[i + 2]) {
      // A pixel is grayscale if all three color channels are the same. If any pixel is not grayscale, that means the browser
      // rendered the flag emoji image instead of the fallback text, so we can return true.
      return true;
    }
  }

  return false;
};

In the project I was working on where we encountered this issue, we decided to not bother with fallbacks and just hide the emoji altogether if we detect that the browser doesn’t support it. It’s disappointing but it’s fine.

I think the biggest lesson here is that you should never make flag emojis an integral part of your site’s design, or you will suffer. In an ideal world, this would be fixed someday, but it’s been years with no movement so I’m not holding my breath. Thanks, Microsoft! 🇹🇼