Unicode flag emoji work fine on Mac and Linux but show as two letter codes on Windows. Here is the quick fix that works everywhere.
Unicode country flag emoji work by combining two regional indicator letters. The Thai flag is the combination of T and H regional indicators, for example. On macOS and most Linux systems, the OS font stack includes a color emoji font that renders these combinations as the correct flag image.
Windows does not. Windows 11 and earlier do not ship with a font that renders regional indicator sequences as flag emoji. Users on Windows see something like TH or a small box instead of the actual flag.
If you are building anything with country selectors, leaderboards, or user location features, this will come up.
The fix is to replace Unicode flags with flag images. The cleanest source for this is flagcdn.com, which serves SVG and PNG flags by ISO country code:
https://flagcdn.com/w20/th.png (20px wide PNG)
https://flagcdn.com/th.svg (SVG)In React, a simple utility component:
function Flag({ countryCode, size = 20 }: { countryCode: string; size?: number }) {
const code = countryCode.toLowerCase();
return (
<img
src={`https://flagcdn.com/w${size}/${code}.png`}
srcSet={`https://flagcdn.com/w${size * 2}/${code}.png 2x`}
width={size}
height={size * 0.75}
alt={`${countryCode} flag`}
loading="lazy"
/>
);
}The srcSet attribute gives retina displays the higher resolution version automatically.
If you want to self-host the flags to avoid the external dependency, flagcdn.com's assets are available on npm as the flag-icons package:
npm install flag-iconsThen you can serve them from your own CDN or just import the CSS which handles everything via CSS classes.
The image approach works identically on Windows, Mac, Linux, Android, and iOS. The flagcdn.com CDN is reliable and the response headers include proper caching so the images do not get fetched repeatedly.
One small gotcha: make sure you are using lowercase ISO 3166-1 alpha-2 codes. flagcdn.com accepts only lowercase and will 404 on uppercase. A simple .toLowerCase() on the country code before constructing the URL handles this.