Flag Emoji Validator
Check if an emoji is a valid country flag (Regional Indicator Symbols). Shows ISO 3166-1 alpha-2.
Flag emoji validation: how Unicode encodes a country in two characters
A flag emoji like 🇧🇷 is not a single character in Unicode — it is a pair of Regional Indicator Symbol Letters (RIS) that together form the ISO 3166-1 alpha-2 country code. The flag of Brazil is the letters B + R rendered as a sequence: 🇧 (U+1F1E7) followed by 🇷 (U+1F1F7). The flag of the United States is 🇺 + 🇸 (U+1F1FA + U+1F1F8). The font and operating system are the only thing that turns the pair into an actual flag glyph — at the byte level it is just two indicator letters.
This means flag validation has two layers: (1) is the input syntactically two Regional Indicator codepoints, and (2) does the resulting alpha-2 pair correspond to a recognised ISO 3166-1 country? A string can pass the first check and still fail the second — combinations like 🇽🇽 (XX) are well-formed sequences but render as the two raw letters because no country has that code.
The Regional Indicator block: U+1F1E6 to U+1F1FF
Unicode reserves 26 codepoints in the Supplementary Multilingual Plane for the Regional Indicators:
U+1F1E6= 🇦,U+1F1E7= 🇧,U+1F1E8= 🇨, ...U+1F1FF= 🇿- Each indicator is 4 bytes in UTF-8 — a flag emoji is therefore 8 bytes, twice the length of a basic emoji.
String.lengthin JavaScript returns 4 for a single flag (two surrogate pairs), not 1.- To count flags correctly use the iterator protocol:
[...'🇧🇷'].length === 2, then group pairs.
The validation regex with the Unicode flag is direct:
const flagRegex = /^[\u{1F1E6}-\u{1F1FF}]{2}$/u
function isFlagEmoji(str) {
if (!flagRegex.test(str)) return false
const chars = [...str]
const a = chars[0].codePointAt(0) - 0x1F1E6
const b = chars[1].codePointAt(0) - 0x1F1E6
const code = String.fromCharCode(65 + a, 65 + b) // alpha-2
return ISO_3166_1.has(code)
}
Subdivisions, special flags and Tag Sequences
A handful of flags fall outside the Regional Indicator pattern:
- England 🏴, Scotland 🏴, Wales 🏴 — use a Tag Sequence: a black flag base (
U+1F3F4) plus invisible Tag characters spelling out the ISO 3166-2 subdivision code (gb-eng,gb-sct,gb-wls), terminated by U+E007F. - Pirate flag 🏴☠️ — black flag + ZWJ (Zero-Width Joiner) + skull and crossbones.
- Rainbow flag 🏳️🌈 — white flag + variation selector + ZWJ + rainbow.
- Transgender flag 🏳️⚧️ — same ZWJ pattern with the gender symbol.
The total of country/territory flags is roughly 250, matching the active ISO 3166-1 list. The Unicode CLDR (Common Locale Data Repository) provides localised country names for each — useful when you want to show "Brasil" next to 🇧🇷 in a Portuguese UI and "Brazil" in an English UI.
Platform inconsistencies and political sensitivities
Rendering is platform-dependent and politically loaded. Apple's emoji font ships real flag glyphs for every country; Google's Noto Color Emoji does the same. Microsoft Windows historically rendered the bare Regional Indicator letters instead of a glyph — that finally changed with Windows 11. The Taiwan flag 🇹🇼 is the canonical example: on iOS the device hides it when the system region is set to mainland China; some Chinese-market Android builds skip it entirely. 🇽🇰 Kosovo was added late and is still inconsistent. 🇪🇺 European Union uses the special code EU that is technically reserved, not assigned.
For applications, this means: never assume the same flag emoji will render identically on every device. Show the country name as a fallback, especially in accessibility contexts where screen readers may announce only the alpha-2 code.
Use cases in Brazilian apps
Flag emojis appear in language switchers (🇧🇷 / 🇺🇸 / 🇪🇸), country pickers in e-commerce checkout, WhatsApp Status, Instagram bios, push-notification badges and CDN region indicators. The biggest pitfall is using a flag to indicate a language instead of a country — Spanish is spoken in 21 countries, and choosing 🇪🇸 over 🇲🇽 is a political signal users notice. The safe pattern is to label the option by language code (pt-BR, en-US) and put the flag next to it as decoration, never as the sole identifier.
FAQ
How many country flags exist? About 250, matching ISO 3166-1. Subdivisions add a few more via Tag Sequences (England, Scotland, Wales, Texas in some fonts), and special flags (pirate, rainbow, transgender) use ZWJ sequences.
Why does my code see length 4 for 🇧🇷? JavaScript strings are UTF-16. Each Regional Indicator is in the supplementary plane and occupies two surrogate code units, so a flag is 4 UTF-16 code units. Use [...str] or str.codePointAt to count actual characters.
Why does the Taiwan flag disappear on some devices? Apple and some Chinese-market Android builds hide 🇹🇼 when the device region is set to mainland China, for regulatory reasons. The underlying codepoint sequence is still there in the data — only the rendered glyph changes.
Are subdivision flags Tag Sequences? Yes. England, Scotland and Wales use the black-flag base plus ISO 3166-2 codes in Tag characters. Most fonts render them as the regional flag; older fonts fall back to the bare black flag.
Can I validate without an ISO 3166 table? Only structurally — you can confirm the input is two Regional Indicators, but you cannot tell whether the pair encodes a real country without the lookup list. Combinations like 🇽🇽 pass the regex but are not flags.
Related Tools
Portugal NIF Validator
Validate Portuguese NIF (Tax ID) — mod 11 check digit and valid prefixes (1, 2, 3, 5, 6, 8, 9).
Hex Color Validator
Validate hex colors in #RGB, #RGBA, #RRGGBB or #RRGGBBAA format and convert between them. Everything in your browser.
Mexico RFC Validator
Validate Mexican RFC (Tax ID) — individual (13 chars) or company (12 chars). Format and check digit.
Base32 Crockford Validator
Check if a string uses only the Base32 Crockford alphabet (omits I, L, O, U to avoid ambiguity). Common in human-readable IDs.
RENAVAM Validator
Validate Brazilian RENAVAM vehicle registration numbers using the official DENATRAN algorithm. No data sent to servers.
Base91 Validator
Check if a string uses only Base91 characters (letters, digits and specific symbols like !#$%&()*+...). Format only.