<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rushan</title>
    <description>The latest articles on DEV Community by Rushan (@rushan123).</description>
    <link>https://dev.to/rushan123</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F316291%2F178e59ff-4dcf-4a4b-baf9-27600b6ebc07.png</url>
      <title>DEV Community: Rushan</title>
      <link>https://dev.to/rushan123</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rushan123"/>
    <language>en</language>
    <item>
      <title>Making SVG icons screen-reader accessible: titles, aria-labels, and unique IDs</title>
      <dc:creator>Rushan</dc:creator>
      <pubDate>Tue, 11 Aug 2026 13:37:39 +0000</pubDate>
      <link>https://dev.to/rushan123/making-svg-icons-screen-reader-accessible-titles-aria-labels-and-unique-ids-9g5</link>
      <guid>https://dev.to/rushan123/making-svg-icons-screen-reader-accessible-titles-aria-labels-and-unique-ids-9g5</guid>
      <description>&lt;p&gt;When you build an interface, icons are the part you check with your eyes and then stop thinking about. You drop an &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; into a button, it looks great, and you move on. A screen reader user gets a different experience: either nothing where a label belonged, or an announcement of "graphic" on an icon that should have stayed quiet.&lt;/p&gt;

&lt;p&gt;Labeling every icon is the wrong fix, because half of them sit next to text that already says the same thing. For each icon you need to decide whether it carries meaning on its own, and then mark it accordingly. That decision has two correct outcomes and one common mistake, and once you name an icon you inherit a second problem: the &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; element needs an id, and ids must be unique on the page.&lt;/p&gt;

&lt;p&gt;In this article we'll walk through both outcomes with real markup, see what an id collision does to a screen reader, and use GeoIcons, a premium library of geographic map-shape icons, to get both behaviors from a single prop.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An icon is either decorative (&lt;code&gt;aria-hidden="true"&lt;/code&gt;, skipped by screen readers) or meaningful (&lt;code&gt;role="img"&lt;/code&gt; plus an accessible name). Every icon should land in one of those two states.&lt;/li&gt;
&lt;li&gt;Every GeoIcon is decorative by default. Pass &lt;code&gt;aria-label&lt;/code&gt; and it switches to meaningful, adding &lt;code&gt;role="img"&lt;/code&gt;, a &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;, and &lt;code&gt;aria-labelledby&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Each icon generates a unique id for its &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;, through React's &lt;code&gt;useId()&lt;/code&gt; and the equivalent in the Vue, Angular, and vanilla packages, so rendering the same icon twice never produces duplicate ids.&lt;/li&gt;
&lt;li&gt;Inside an icon-only button, label the button and leave the icon decorative. Two names on one control is worse than one.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why is my SVG icon invisible to screen readers?
&lt;/h2&gt;

&lt;p&gt;An &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; element has no implicit accessible name or useful default role. The browser sees a graphic without any text to announce, so there is nothing meaningful for a screen reader to read. VoiceOver and NVDA may skip the icon entirely or announce something vague like "image" without a name.&lt;/p&gt;

&lt;p&gt;In many cases, this is exactly what you want. If an icon appears next to the word "France", a screen reader ignoring the icon will simply read "France" once, which conveys the same single piece of information a sighted user takes from the row.&lt;/p&gt;

&lt;p&gt;The problem comes when the icon is the only thing communicating information. If the icon is meaningful on its own, a screen reader needs a way to identify it.&lt;/p&gt;

&lt;p&gt;So, for each icon, ask yourself: &lt;strong&gt;does this icon communicate information that the surrounding text does not?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer determines how you should mark up the SVG.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decorative icons should say nothing
&lt;/h2&gt;

&lt;p&gt;If nearby text already conveys the meaning, the icon is decorative. Mark it &lt;code&gt;aria-hidden="true"&lt;/code&gt; so assistive technology skips it and reads the text instead.&lt;/p&gt;

&lt;p&gt;With GeoIcons this is the default. Render an icon with no &lt;code&gt;aria-label&lt;/code&gt; and you get a hidden graphic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Fr&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/react/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Fr&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; France
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That produces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt; &lt;span class="na"&gt;stroke=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt;
     &lt;span class="na"&gt;stroke-width=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt; &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;stroke-linejoin=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"m6.356 17.388…"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A screen reader reads "France" once. Without &lt;code&gt;aria-hidden&lt;/code&gt;, some setups would announce a nameless graphic before the word, which adds noise and tells the user nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meaningful icons need an accessible name
&lt;/h2&gt;

&lt;p&gt;If the icon stands alone, it needs a name. Pass &lt;code&gt;aria-label&lt;/code&gt; and you turn a decoration into a named image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Fr&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"France"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That produces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt; &lt;span class="na"&gt;stroke=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt;
     &lt;span class="na"&gt;stroke-width=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"img"&lt;/span&gt; &lt;span class="na"&gt;aria-labelledby=&lt;/span&gt;&lt;span class="s"&gt;"_r_7_-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"_r_7_-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;France&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;stroke-linejoin=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"m6.356 17.388…"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;_r_7_-title&lt;/code&gt; id looks cryptic, and that is fine. A screen reader never reads the id, it reads the text inside the &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;, so "France" is what the user hears. The id exists only to connect the two elements, which is why a generated value beats a readable one like &lt;code&gt;france-title&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Three things changed together, and each one matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;role="img"&lt;/code&gt;&lt;/strong&gt; tells assistive technology to treat the SVG as a single image instead of a container of shapes. Without it, support for naming a bare &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; has historically been uneven across screen readers, and some will still expose the graphic as a group rather than an image.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;&lt;/strong&gt; is the SVG element that holds the accessible name. It is the SVG equivalent of an &lt;code&gt;alt&lt;/code&gt; attribute, and it is not rendered visually. Browsers do surface it as a tooltip on hover, which is a small bonus rather than the point.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;aria-labelledby&lt;/code&gt;&lt;/strong&gt; points at the &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; by id, which is the association that gives the image its name.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You pass one prop and get all three.&lt;/p&gt;

&lt;p&gt;Here are both treatments side by side. The first row pairs a decorative icon with a visible label, and the second row leaves the icon alone, carrying its own name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Fr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Jp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/react/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Decorative: the visible label names the row, so the icon stays hidden.&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center gap-2"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Fr&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;France&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// Meaningful: nothing else names it, so the icon carries the name.&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Jp&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Japan"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://geoicons.io/blog/svg-icons-screen-reader-accessible" rel="noopener noreferrer"&gt;See it live on geoicons.io →&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why does the &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; need a unique id?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;aria-labelledby&lt;/code&gt; names an icon by pointing at an id, and ids must be unique in a document. If two icons on the same page both render &lt;code&gt;&amp;lt;title id="fr-title"&amp;gt;&lt;/code&gt;, the browser resolves that reference to whichever one comes first. Every icon sharing the id then announces the first icon's name.&lt;/p&gt;

&lt;p&gt;A country list, a filter bar, and a footer will happily render the same icon three times. With a hardcoded id, two of those three announce the wrong name. Duplicate ids are also invalid HTML, so validators, linters, and some testing tools flag the problem before a user hits it.&lt;/p&gt;

&lt;p&gt;Every GeoIcon calls React's &lt;a href="https://react.dev/reference/react/useId" rel="noopener noreferrer"&gt;&lt;code&gt;useId()&lt;/code&gt;&lt;/a&gt; and namespaces its &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; id with the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// …&lt;/span&gt;
&lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;labelledby&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ariaLabel&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-title`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// …&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ariaLabel&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-title`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ariaLabel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;useId()&lt;/code&gt; returns a value that is unique per component instance and stable between the server render and the client hydration, so the ids match on both sides and React does not warn about a mismatch. The exact string is a React implementation detail. React 19 generates ids like &lt;code&gt;_r_7_&lt;/code&gt; in the browser and &lt;code&gt;_R_2_&lt;/code&gt; when the markup comes from the server, and React 18 produced &lt;code&gt;:r7:&lt;/code&gt; instead, so never write a selector or a test that depends on the shape. Render the same icon twice and you get two different ids:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Fr&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"France"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Fr&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"France, selected"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"img"&lt;/span&gt; &lt;span class="na"&gt;aria-labelledby=&lt;/span&gt;&lt;span class="s"&gt;"_r_7_-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"_r_7_-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;France&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"img"&lt;/span&gt; &lt;span class="na"&gt;aria-labelledby=&lt;/span&gt;&lt;span class="s"&gt;"_r_8_-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"_r_8_-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;France, selected&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two IDs are different, so each &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; stays linked to its own icon. A screen reader announces "France" for the first one and "France, selected" for the second. This continues to work correctly no matter how many copies of the icon appear on the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Icon-only buttons: label the control, hide the icon
&lt;/h2&gt;

&lt;p&gt;The most common accessibility bug in icon work is doubling up the name. You put an &lt;code&gt;aria-label&lt;/code&gt; on the button because it has no visible text, then put another one on the icon inside it, and a screen reader user hears the name twice.&lt;/p&gt;

&lt;p&gt;Label the button, and leave the icon decorative:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Filter by France"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Fr&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The button announces "Filter by France, button". The icon inside stays &lt;code&gt;aria-hidden="true"&lt;/code&gt; and adds nothing.&lt;/p&gt;

&lt;p&gt;The same rule covers links, menu items, and toolbar controls. Whichever element the user clicks or perceives as one unit is the element that carries the name, and everything inside it is decoration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Link with visible text: the text is the name, the icon is decorative.&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/icons/de"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;De&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; Germany
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// Standalone status indicator: nothing else names it, so the icon does.&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Jp&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Shipping from Japan"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The same rules in Vue, Angular, and vanilla JS
&lt;/h2&gt;

&lt;p&gt;All four GeoIcons packages behave the same way here, since the rules live in the rendered markup rather than in any one framework. No &lt;code&gt;aria-label&lt;/code&gt; gives you a hidden icon, and an &lt;code&gt;aria-label&lt;/code&gt; gives you &lt;code&gt;role="img"&lt;/code&gt;, a &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;, and a unique id. The Vue and vanilla packages also leave your &lt;code&gt;aria-label&lt;/code&gt; on the &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; next to the generated &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;, which changes nothing for the user, because &lt;code&gt;aria-labelledby&lt;/code&gt; wins the name computation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Fr&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/vue/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Decorative --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Fr&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; France

  &lt;span class="c"&gt;&amp;lt;!-- Meaningful --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Fr&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"France"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vue uses its own &lt;code&gt;useId()&lt;/code&gt; on Vue 3.5 and later, and falls back to a per-instance uid on earlier versions, so the id stays unique either way.&lt;/p&gt;

&lt;p&gt;Angular follows the same contract. Import the icon into your standalone component, then use its element selector in the template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Fr&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/angular/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// @Component({ standalone: true, imports: [Fr], template: `…` })&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Decorative --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;geoicon-fr&amp;gt;&amp;lt;/geoicon-fr&amp;gt;&lt;/span&gt; France

&lt;span class="c"&gt;&amp;lt;!-- Meaningful --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;geoicon-fr&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"France"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/geoicon-fr&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Angular has no &lt;code&gt;useId()&lt;/code&gt;, so the package supplies its own &lt;code&gt;GeoIconIdService&lt;/code&gt;. It is provided in root, so Angular Universal creates a fresh instance per server-side request and the counter restarts at zero for every render. The server and client id sequences line up, and hydration produces no mismatch warnings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Fr&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/vanilla/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Decorative&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Fr&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Meaningful&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Fr&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;France&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The vanilla factory keeps a module-level counter and builds the &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; element with &lt;code&gt;createElementNS&lt;/code&gt;, then inserts it as the first child of the &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I verify it works?
&lt;/h2&gt;

&lt;p&gt;Read the accessibility tree rather than the DOM. The DOM shows you which attributes you wrote, while the accessibility tree shows you the role and the name a screen reader will receive.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chrome DevTools:&lt;/strong&gt; open the Elements panel, select the icon, and look at the Accessibility pane. A decorative icon shows as ignored, with &lt;code&gt;aria-hidden&lt;/code&gt; given as the reason. A meaningful icon shows the role &lt;code&gt;image&lt;/code&gt; and the name you passed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A real screen reader:&lt;/strong&gt; turn on VoiceOver with Cmd+F5 on macOS, or NVDA on Windows, and tab through the component. Your icon buttons should announce one name each, not two, and not none.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated checks:&lt;/strong&gt; axe DevTools flags a duplicate id that ARIA points at, through its &lt;code&gt;duplicate-id-aria&lt;/code&gt; rule, and both axe and Lighthouse flag a button with no accessible name. Axe's general &lt;code&gt;duplicate-id&lt;/code&gt; rule is deprecated and off by default, so an HTML validator remains the tool that catches every collision. Neither one can tell you that "France" was the wrong name for an icon that meant "shipping origin", so keep reading your labels yourself.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://geoicons.io/blog/svg-icons-screen-reader-accessible" rel="noopener noreferrer"&gt;See it live on geoicons.io →&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Should I use aria-label or a  element on an SVG icon?
&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;Use a &amp;lt;title&amp;gt; element referenced by aria-labelledby, which is what GeoIcons builds for you. You pass an aria-label prop, and the React component uses that string as the &amp;lt;title&amp;gt; text and renders role='img' plus aria-labelledby instead of an aria-label attribute. Passing aria-label alone on a bare &amp;lt;svg&amp;gt; has had uneven screen reader support historically, so the titled reference is the reliable pattern.&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;
  &amp;lt;a name="do-decorative-icons-need-ariahidden-if-they-have-no-label" href="#do-decorative-icons-need-ariahidden-if-they-have-no-label" class="anchor"&amp;gt;
  &amp;lt;/a&amp;gt;
  Do decorative icons need aria-hidden if they have no label?
&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;Yes, and GeoIcons adds it automatically. An unlabeled &amp;lt;svg&amp;gt; can still be announced as a nameless graphic by some screen readers, which adds noise without adding meaning. Setting aria-hidden='true' removes the icon from the accessibility tree entirely, so the nearby text is read once and nothing else.&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;
  &amp;lt;a name="what-happens-if-two-svg-icons-share-the-same-id" href="#what-happens-if-two-svg-icons-share-the-same-id" class="anchor"&amp;gt;
  &amp;lt;/a&amp;gt;
  What happens if two SVG icons share the same &amp;lt;title&amp;gt; id?
&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;The browser resolves aria-labelledby to the first element with that id, so every later icon announces the first icon's name. It is also invalid HTML. GeoIcons avoids this by generating a unique id per instance with React's useId (or the equivalent in the Vue, Angular, and vanilla packages), so the same icon rendered many times on one page never collides.&amp;lt;/p&amp;gt;
&amp;lt;h2&amp;gt;
  &amp;lt;a name="get-started" href="#get-started" class="anchor"&amp;gt;
  &amp;lt;/a&amp;gt;
  Get started
&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;Install the package and label only the icons that carry meaning:&amp;lt;br&amp;gt;
&amp;lt;/p&amp;gt;
&amp;lt;div class="highlight"&amp;gt;&amp;lt;pre class="highlight shell"&amp;gt;&amp;lt;code&amp;gt;npm i &amp;lt;a class="mentioned-user" href="https://dev.to/geoicons"&amp;gt;@geoicons&amp;lt;/a&amp;gt;/react
&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;div class="highlight"&amp;gt;&amp;lt;pre class="highlight tsx"&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class="k"&amp;gt;import&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt; &amp;lt;span class="nx"&amp;gt;Fr&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="nx"&amp;gt;Jp&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt; &amp;lt;span class="k"&amp;gt;from&amp;lt;/span&amp;gt; &amp;lt;span class="dl"&amp;gt;'&amp;lt;/span&amp;gt;&amp;lt;span class="s1"&amp;gt;&amp;lt;a class="mentioned-user" href="https://dev.to/geoicons"&amp;gt;@geoicons&amp;lt;/a&amp;gt;/react/countries&amp;lt;/span&amp;gt;&amp;lt;span class="dl"&amp;gt;'&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;;&amp;lt;/span&amp;gt;

&amp;lt;span class="k"&amp;gt;export&amp;lt;/span&amp;gt; &amp;lt;span class="k"&amp;gt;default&amp;lt;/span&amp;gt; &amp;lt;span class="kd"&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class="nf"&amp;gt;App&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;()&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt;
  &amp;lt;span class="k"&amp;gt;return &amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;
    &amp;lt;span class="p"&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span class="nt"&amp;gt;ul&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
      &amp;lt;span class="si"&amp;gt;{&amp;lt;/span&amp;gt;&amp;lt;span class="cm"&amp;gt;/* Decorative: the text names the row. */&amp;lt;/span&amp;gt;&amp;lt;span class="si"&amp;gt;}&amp;lt;/span&amp;gt;
      &amp;lt;span class="p"&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span class="nt"&amp;gt;li&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span class="nc"&amp;gt;Fr&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;/&amp;gt;&amp;lt;/span&amp;gt; France&amp;lt;span class="p"&amp;gt;&amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span class="nt"&amp;gt;li&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
      &amp;lt;span class="si"&amp;gt;{&amp;lt;/span&amp;gt;&amp;lt;span class="cm"&amp;gt;/* Meaningful: the icon is on its own. */&amp;lt;/span&amp;gt;&amp;lt;span class="si"&amp;gt;}&amp;lt;/span&amp;gt;
      &amp;lt;span class="p"&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span class="nt"&amp;gt;li&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span class="nc"&amp;gt;Jp&amp;lt;/span&amp;gt; &amp;lt;span class="na"&amp;gt;aria-label&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class="s"&amp;gt;"Japan"&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;/&amp;gt;&amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span class="nt"&amp;gt;li&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="p"&amp;gt;&amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span class="nt"&amp;gt;ul&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
  &amp;lt;span class="p"&amp;gt;);&amp;lt;/span&amp;gt;
&amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt;
&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;For the full accessibility contract of every icon, see &amp;lt;a href="https://geoicons.io/docs/accessibility"&amp;gt;the accessibility docs&amp;lt;/a&amp;gt;, and for every prop each icon accepts, see &amp;lt;a href="https://geoicons.io/docs/api"&amp;gt;the GeoIcons API reference&amp;lt;/a&amp;gt;. New here? Start with &amp;lt;a href="https://geoicons.io/blog/add-country-icons-react"&amp;gt;adding your first country icon&amp;lt;/a&amp;gt;, see how &amp;lt;a href="https://geoicons.io/blog/dark-mode-icons"&amp;gt;currentColor keeps icons readable in dark mode&amp;lt;/a&amp;gt;, or &amp;lt;a href="https://geoicons.io/icons/countries"&amp;gt;browse all 422 icons&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;

&lt;/h3&gt;

</description>
      <category>a11y</category>
      <category>svgicons</category>
      <category>screenreaders</category>
      <category>aria</category>
    </item>
    <item>
      <title>ISO 3166-1 Alpha-2 Country Codes: A Developer's Guide</title>
      <dc:creator>Rushan</dc:creator>
      <pubDate>Wed, 29 Jul 2026 03:46:53 +0000</pubDate>
      <link>https://dev.to/rushan123/iso-3166-1-alpha-2-country-codes-a-developers-guide-e20</link>
      <guid>https://dev.to/rushan123/iso-3166-1-alpha-2-country-codes-a-developers-guide-e20</guid>
      <description>&lt;p&gt;Any application that ships across borders needs a way to name a country. You reach for a two-letter code, write &lt;code&gt;US&lt;/code&gt;, &lt;code&gt;JP&lt;/code&gt;, &lt;code&gt;DE&lt;/code&gt;, and move on.&lt;/p&gt;

&lt;p&gt;Then a support ticket arrives. A user in Belfast picked "United Kingdom" and your shipping API rejected &lt;code&gt;UK&lt;/code&gt;. Someone in Pristina found no option at all. Your analytics dashboard shows a country called &lt;code&gt;AN&lt;/code&gt; that dissolved in 2010.&lt;/p&gt;

&lt;p&gt;These bugs share one root: ISO 3166-1 alpha-2 carries more rules than its two characters suggest. Let's walk through the parts that break real applications, and how to model country data so the next revision of the standard does not break yours.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ISO 3166-1 defines 249 officially assigned alpha-2 codes. &lt;code&gt;UK&lt;/code&gt; is not one of them. The United Kingdom is &lt;code&gt;GB&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Four other status categories exist: user-assigned, exceptionally reserved, transitionally reserved, and indeterminately reserved. They follow different rules.&lt;/li&gt;
&lt;li&gt;Kosovo uses &lt;code&gt;XK&lt;/code&gt;, a code from the user-assigned range that ISO has never officially assigned.&lt;/li&gt;
&lt;li&gt;Codes get recycled. &lt;code&gt;CS&lt;/code&gt; meant Czechoslovakia, then Serbia and Montenegro.&lt;/li&gt;
&lt;li&gt;Country names change far more often than their codes. Store the code, resolve the name at render time.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What alpha-2 covers
&lt;/h2&gt;

&lt;p&gt;ISO 3166 splits into three parts. Part 1 names countries and their dependent territories. Part 2 names subdivisions inside them. Part 3 records codes that fell out of use.&lt;/p&gt;

&lt;p&gt;Part 1 gives you three code sets for the same entity:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Japan&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Alpha-2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;JP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Two letters. Used by ccTLDs, BCP 47 language tags, payment APIs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alpha-3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;JPN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Three letters. Easier to read on its own.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Numeric-3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;392&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Digits from UN M49. Script-independent, survives alphabet changes.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Alpha-2 is the set you meet most often. Two characters fit anywhere, and the Internet Assigned Numbers Authority (IANA) draws the country-code top-level domains straight from the alpha-2 list, which puts these codes in front of everyone who ever registered a domain. That reach explains the misuse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five kinds of code
&lt;/h2&gt;

&lt;p&gt;The 249 official codes get the attention. Most bugs come from the reserved ranges.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;th&gt;What you do with them&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Officially assigned&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;US&lt;/code&gt;, &lt;code&gt;JP&lt;/code&gt;, &lt;code&gt;DE&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Use these. 249 of them.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exceptionally reserved&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;EU&lt;/code&gt;, &lt;code&gt;UK&lt;/code&gt;, &lt;code&gt;UN&lt;/code&gt;, &lt;code&gt;AC&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Reserved for a specific requester. Never repurpose them.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transitionally reserved&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;AN&lt;/code&gt;, &lt;code&gt;CS&lt;/code&gt;, &lt;code&gt;YU&lt;/code&gt;, &lt;code&gt;BU&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Retired countries. Keep them readable in old records, block them in new input.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Indeterminately reserved&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;RI&lt;/code&gt;, &lt;code&gt;WG&lt;/code&gt;, &lt;code&gt;FL&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Vehicle registration codes that clash with ISO. Ignore.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User-assigned&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;AA&lt;/code&gt;, &lt;code&gt;QM&lt;/code&gt;–&lt;code&gt;QZ&lt;/code&gt;, &lt;code&gt;XA&lt;/code&gt;–&lt;code&gt;XZ&lt;/code&gt;, &lt;code&gt;ZZ&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Yours to use. ISO promises never to assign them.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That last row deserves a bookmark. When you need a code for something ISO does not cover, an internal test region or a disputed territory, take one from the user-assigned range. Inventing a code outside that range, &lt;code&gt;ZQ&lt;/code&gt; or &lt;code&gt;OJ&lt;/code&gt;, guarantees a collision the day ISO assigns it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The United Kingdom is GB
&lt;/h2&gt;

&lt;p&gt;This is the mistake that reaches production most often. The ISO code for the United Kingdom of Great Britain and Northern Ireland is &lt;code&gt;GB&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;UK&lt;/code&gt; exists in the standard, but only as an exceptional reservation. The reservation exists to accommodate &lt;code&gt;.uk&lt;/code&gt;, which was already in wide use before ISO codes reached domain names, so the United Kingdom asked ISO to set the code aside rather than assign it elsewhere. It never became a country code, and a strict validator rejects it.&lt;/p&gt;

&lt;p&gt;The confusion has an official source. The European Commission publishes country codes that match ISO with two deliberate exceptions: it writes &lt;code&gt;UK&lt;/code&gt; for the United Kingdom and &lt;code&gt;EL&lt;/code&gt; for Greece. If your data arrives from an EU statistical feed, normalize both before you store anything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;EU_TO_ISO&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;UK&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GB&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;EL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;toIso3166&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;EU_TO_ISO&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run every inbound code through one function like this. Patch each call site instead and the rules drift apart within a quarter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kosovo sits outside the standard
&lt;/h2&gt;

&lt;p&gt;Kosovo has no officially assigned alpha-2 code. ISO assigns codes to entities that are UN members or appear on the UN Statistics Division's country list, and Kosovo's status blocks both routes.&lt;/p&gt;

&lt;p&gt;In practice, &lt;code&gt;XK&lt;/code&gt; won. The European Commission, the IMF, and SWIFT all use it, and it comes from the user-assigned &lt;code&gt;XA&lt;/code&gt;–&lt;code&gt;XZ&lt;/code&gt; range, so ISO will never hand it to another country. GeoIcons ships the shape as &lt;code&gt;Xk&lt;/code&gt; for the same reason.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Xk&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/react/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Xk&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Kosovo"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://geoicons.io/blog/iso-3166-1-alpha-2-country-codes" rel="noopener noreferrer"&gt;See it live on geoicons.io →&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If your validator checks incoming codes against a strict ISO list, &lt;code&gt;XK&lt;/code&gt; fails and Kosovo disappears from your form. Add it to the allowlist and document why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Codes get recycled
&lt;/h2&gt;

&lt;p&gt;A retired code moves to transitionally reserved status, where ISO intends to hold it for at least 50 years before it returns to circulation. Intends, not guarantees.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CS&lt;/code&gt; made the round trip in ten. It belonged to Czechoslovakia until 1993, and in 2003 it came back as Serbia and Montenegro. That union dissolved in 2006, so &lt;code&gt;CS&lt;/code&gt; sits reserved again while &lt;code&gt;RS&lt;/code&gt; and &lt;code&gt;ME&lt;/code&gt; carry the successor states.&lt;/p&gt;

&lt;p&gt;The Netherlands Antilles produced a cleaner split. When &lt;code&gt;AN&lt;/code&gt; dissolved in October 2010, three codes replaced it: &lt;code&gt;CW&lt;/code&gt; for Curaçao, &lt;code&gt;SX&lt;/code&gt; for Sint Maarten, and &lt;code&gt;BQ&lt;/code&gt; for Bonaire, Sint Eustatius and Saba.&lt;/p&gt;

&lt;p&gt;Two consequences for your schema. A country code identifies a country at a point in time, so an old row carrying &lt;code&gt;AN&lt;/code&gt; needs a date to interpret it. And a code you resolve to a name today may resolve to a different name in a decade, which rules out caching the pair as if it were permanent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Names change, codes hold
&lt;/h2&gt;

&lt;p&gt;The code is the stable half of the pair, and the name is the half that moves.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;th&gt;Code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2013&lt;/td&gt;
&lt;td&gt;Cape Verde becomes Cabo Verde&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CV&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;td&gt;Czech Republic adds the short name Czechia&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CZ&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;td&gt;Swaziland becomes Eswatini&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SZ&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;Macedonia becomes North Macedonia&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MK&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2022&lt;/td&gt;
&lt;td&gt;Turkey becomes Türkiye&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TR&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Five renames in a decade, zero code changes. Store &lt;code&gt;TR&lt;/code&gt; in your database and resolve the display name from a library you can update, such as &lt;code&gt;i18n-iso-countries&lt;/code&gt;. Store "Turkey" as a string and you own a migration.&lt;/p&gt;

&lt;p&gt;Search deserves the same treatment. A user who learned "Swaziland" in school will type it, so keep the old names as aliases even after the label changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subdivisions live in part 2
&lt;/h2&gt;

&lt;p&gt;England, Scotland, Wales, and Northern Ireland have no alpha-2 codes. They are subdivisions of &lt;code&gt;GB&lt;/code&gt; under ISO 3166-2, written &lt;code&gt;GB-ENG&lt;/code&gt;, &lt;code&gt;GB-SCT&lt;/code&gt;, &lt;code&gt;GB-WLS&lt;/code&gt;, and &lt;code&gt;GB-NIR&lt;/code&gt;. US states follow the same pattern: California is &lt;code&gt;US-CA&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Users care about this distinction more than standards bodies do. A Scottish customer filling in a shipping form wants to see Scotland. Your payment processor wants &lt;code&gt;GB&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Handle both. Show the subdivision in the interface, send the country code to the API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Scotland&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/react/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;REGION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GB-SCT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Scotland&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GB&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center gap-2"&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;REGION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;data-region&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;REGION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Scotland&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;REGION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GeoIcons ships these four as &lt;code&gt;England&lt;/code&gt;, &lt;code&gt;Scotland&lt;/code&gt;, &lt;code&gt;Wales&lt;/code&gt;, and &lt;code&gt;NorthernIreland&lt;/code&gt; rather than forcing them into a code the standard does not define.&lt;/p&gt;

&lt;h2&gt;
  
  
  Regions have no country code at all
&lt;/h2&gt;

&lt;p&gt;The European Union is not a country, so it holds &lt;code&gt;EU&lt;/code&gt; as an exceptional reservation. ASEAN, Mercosur, and the GCC hold nothing. Neither does a shipping zone you invented last week.&lt;/p&gt;

&lt;p&gt;Trade blocs and continents belong in a separate namespace from countries. Mixing an &lt;code&gt;EU&lt;/code&gt; row into a table of ISO codes means every consumer of that table now needs to know which rows are real countries.&lt;/p&gt;

&lt;p&gt;GeoIcons keeps the same separation. Countries import from &lt;code&gt;@geoicons/react/countries&lt;/code&gt;, regions from &lt;code&gt;@geoicons/react/areas&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UnitedKingdom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EuropeanUnionEu&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/react/areas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UnitedKingdom&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"United Kingdom"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EuropeanUnionEu&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"European Union"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://geoicons.io/blog/iso-3166-1-alpha-2-country-codes" rel="noopener noreferrer"&gt;See it live on geoicons.io →&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The United Kingdom is the one entity that does not ship as a country shape. There is no &lt;code&gt;Gb&lt;/code&gt; export. The four constituents live in &lt;code&gt;countries&lt;/code&gt; as &lt;code&gt;England&lt;/code&gt;, &lt;code&gt;Scotland&lt;/code&gt;, &lt;code&gt;Wales&lt;/code&gt;, and &lt;code&gt;NorthernIreland&lt;/code&gt;, and the union itself ships as an area shape, &lt;code&gt;UnitedKingdom&lt;/code&gt;, alongside &lt;code&gt;GreatBritain&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rules for your codebase
&lt;/h2&gt;

&lt;p&gt;Six habits that prevent most country-data bugs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Store the alpha-2 code, not the name.&lt;/strong&gt; &lt;code&gt;CHAR(2)&lt;/code&gt; is enough. Resolve names through a library at render time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uppercase on the way in.&lt;/strong&gt; The standard writes codes in uppercase. Lowercase shows up in domains and URLs, so normalize once at the boundary and compare case-insensitively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate against a real list.&lt;/strong&gt; A regex of &lt;code&gt;^[A-Z]{2}$&lt;/code&gt; accepts &lt;code&gt;ZQ&lt;/code&gt;. Check membership in the 249 official codes plus the exceptions you choose to allow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep an explicit exception list.&lt;/strong&gt; &lt;code&gt;XK&lt;/code&gt; for Kosovo, &lt;code&gt;EL&lt;/code&gt; and &lt;code&gt;UK&lt;/code&gt; from EU feeds. Write the reasons in a comment next to the list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do not derive flag emoji from codes without a fallback.&lt;/strong&gt; Regional indicator pairs produce nothing for &lt;code&gt;UK&lt;/code&gt;, &lt;code&gt;XK&lt;/code&gt;, or any retired code, and Windows renders the letters instead of a graphic for every flag. Vector map outlines dodge both problems, which we covered in &lt;a href="https://geoicons.io/blog/map-shapes-vs-flag-emojis" rel="noopener noreferrer"&gt;map shapes versus flag emojis&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version your reference data.&lt;/strong&gt; Note which revision of the ISO list your snapshot came from, so a diff against next year's list takes minutes.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How GeoIcons maps to the standard
&lt;/h2&gt;

&lt;p&gt;The library carries 255 country shapes. Most of them use the alpha-2 code in PascalCase as the export name, so &lt;code&gt;US&lt;/code&gt; becomes &lt;code&gt;Us&lt;/code&gt; and &lt;code&gt;JP&lt;/code&gt; becomes &lt;code&gt;Jp&lt;/code&gt;. The rest follow the edge cases described above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;UK constituents&lt;/strong&gt; ship under their names: &lt;code&gt;England&lt;/code&gt;, &lt;code&gt;Scotland&lt;/code&gt;, &lt;code&gt;Wales&lt;/code&gt;, &lt;code&gt;NorthernIreland&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kosovo&lt;/strong&gt; ships as &lt;code&gt;Xk&lt;/code&gt;, matching the code the financial world settled on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Caribbean Netherlands&lt;/strong&gt; splits by island, since &lt;code&gt;BQ&lt;/code&gt; covers three: &lt;code&gt;BqBonaire&lt;/code&gt; and &lt;code&gt;BqSintEustatius&lt;/code&gt; ship today.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mainland variants&lt;/strong&gt; drop distant territories for selectors that need a compact shape: &lt;code&gt;FrMainland&lt;/code&gt;, &lt;code&gt;AuMainland&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every shape is a separate named export, so importing &lt;code&gt;Us&lt;/code&gt; and &lt;code&gt;Jp&lt;/code&gt; pulls two icons into your bundle and leaves the other 253 behind. Read &lt;a href="https://geoicons.io/blog/tree-shaking-icons" rel="noopener noreferrer"&gt;why an icon library doesn't have to bloat your bundle&lt;/a&gt; for how that works.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How many ISO 3166-1 alpha-2 codes are there?
&lt;/h3&gt;

&lt;p&gt;249 codes are officially assigned to countries and dependent territories. Additional codes exist as user-assigned, exceptionally reserved, transitionally reserved, or indeterminately reserved entries, but none of those identify a current country.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is the country code for the United Kingdom GB or UK?
&lt;/h3&gt;

&lt;p&gt;GB. The code UK is exceptionally reserved because the .uk domain was already established when ISO codes reached the internet, and it is never an officially assigned country code. The European Commission uses UK in its own publications, which is why the wrong value spreads through EU data feeds.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Kosovo's ISO country code?
&lt;/h3&gt;

&lt;p&gt;Kosovo has no officially assigned code. XK, drawn from the user-assigned XA to XZ range, is the de facto standard used by the European Commission, the IMF, and SWIFT. ISO will never assign XK to another country, so it is safe to adopt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I store alpha-2 or alpha-3 codes?
&lt;/h3&gt;

&lt;p&gt;Alpha-2 for most applications, since it matches ccTLDs, BCP 47 language tags, and the majority of payment and shipping APIs. Choose numeric-3 when your data outlives alphabet changes or crosses scripts, because those codes carry no Latin letters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do ISO country codes ever change?
&lt;/h3&gt;

&lt;p&gt;Codes change far less often than names. Türkiye, Eswatini, and North Macedonia all renamed themselves without changing TR, SZ, or MK. Codes do get retired when a country dissolves. ISO aims to hold a retired code for at least 50 years before reassigning it, but it has broken that guideline: CS moved from Czechoslovakia to Serbia and Montenegro after only ten.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get started
&lt;/h2&gt;

&lt;p&gt;Install GeoIcons and render any country by its ISO code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @geoicons/react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Gr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Tr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Xk&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/react/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Balkans&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center gap-3 text-slate-900 dark:text-slate-100"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Gr&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Greece"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tr&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Türkiye"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Xk&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Kosovo"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browse the full set of 255 country shapes and 167 area shapes in the &lt;a href="https://geoicons.io/icons" rel="noopener noreferrer"&gt;icon catalog&lt;/a&gt;, or read the &lt;a href="https://geoicons.io/docs/api" rel="noopener noreferrer"&gt;API reference&lt;/a&gt; for sizing, color, and accessibility props.&lt;/p&gt;

</description>
      <category>iso3166</category>
      <category>countrycodes</category>
      <category>datamodeling</category>
      <category>geoicons</category>
    </item>
    <item>
      <title>Visual Alignment for Icons and Labels in Tailwind CSS</title>
      <dc:creator>Rushan</dc:creator>
      <pubDate>Fri, 24 Jul 2026 10:41:29 +0000</pubDate>
      <link>https://dev.to/rushan123/visual-alignment-for-icons-and-labels-in-tailwind-css-58jo</link>
      <guid>https://dev.to/rushan123/visual-alignment-for-icons-and-labels-in-tailwind-css-58jo</guid>
      <description>&lt;p&gt;Vector icons do not line up with text out of the box. Unlike letters, graphic paths lack a consistent baseline, ascender, or descender. If you place an icon next to a text label using browser defaults, the shape floats above or below the text baseline.&lt;/p&gt;

&lt;p&gt;We use GeoIcons components for the examples in this post. The library includes clean, optimized map outlines for all countries and regions. Because each shape is a real geographic outline rather than a uniform square glyph, the same alignment rules you would use for any icon apply directly, and each component exposes &lt;code&gt;size&lt;/code&gt; and &lt;code&gt;className&lt;/code&gt; props to make them easy to apply.&lt;/p&gt;

&lt;p&gt;You can fix this visual gap with Tailwind CSS flexbox utilities and relative sizing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flexbox alignment anchors the visual centers of icons and text.&lt;/li&gt;
&lt;li&gt;Sizing utilities map the SVG bounds to the text line-height.&lt;/li&gt;
&lt;li&gt;Translation utilities correct visual offset in asymmetric country shapes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Anchor the visual centers
&lt;/h2&gt;

&lt;p&gt;Use flexbox to align the icon container and the text block. A standard block layout stacks the elements. Raw inline SVG elements line up the bottom edge of the canvas with the text baseline, pushing the icon upward.&lt;/p&gt;

&lt;p&gt;Wrap the icon and the text in a container with flex alignment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Jp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/react/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CountryBadge&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center gap-2"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Jp&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Japan&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;items-center&lt;/code&gt; utility aligns the vertical midpoints of both child elements. The &lt;code&gt;gap-2&lt;/code&gt; utility adds a consistent horizontal spacer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Match icon size to line height
&lt;/h2&gt;

&lt;p&gt;Scale the icon height to match the text line-height. Standard Tailwind body text uses the &lt;code&gt;text-base&lt;/code&gt; class, which carries a font size of 16px and a line-height of 24px. If you set the icon height to 16px, the icon sits within a 24px vertical container, causing a vertical mismatch.&lt;/p&gt;

&lt;p&gt;Set the icon height to match the line-height of the text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* For text-base (24px line-height), use a 20px icon */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center gap-2 text-base leading-6"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Us&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"h-5 w-5"&lt;/span&gt; &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;United States&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* For text-sm (20px line-height), use a 16px icon */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center gap-2 text-sm leading-5"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Us&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"h-4 w-4"&lt;/span&gt; &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;United States&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Choosing an icon height smaller than the line-height leaves space for the border of the icon to sit clear of adjacent text lines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Correct asymmetric visual weight
&lt;/h2&gt;

&lt;p&gt;Every country has its own distinctive proportions. A long, narrow country like Italy or Chile sits differently within its square canvas than a compact country like France or Poland. The browser centers the bounding box of the SVG, but the visual weight of the shape can sit slightly off-center.&lt;/p&gt;

&lt;p&gt;You can correct this offset using relative translation classes on the icon element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;It&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/react/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ItalyListRow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center gap-2"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;It&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"h-5 w-5 translate-y-[1px]"&lt;/span&gt; &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Italy&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;translate-y-px&lt;/code&gt; utility shifts the vector shape down by one pixel, centering the visual weight of the path relative to the uppercase characters of the text label.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put it together: common UI patterns
&lt;/h2&gt;

&lt;p&gt;The rules above (flex centering, size matched to line-height, an optional pixel nudge) carry over to real components. Here are a few patterns you will reach for most often.&lt;/p&gt;

&lt;h3&gt;
  
  
  Buttons
&lt;/h3&gt;

&lt;p&gt;A button already sets its own line-height and padding, so lean on &lt;code&gt;inline-flex items-center&lt;/code&gt; and let the icon inherit the button's text size. Size the icon a touch below the label so it reads as a companion to the text rather than competing with it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Jp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/react/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ShipToButton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;
      &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"inline-flex items-center gap-2 rounded-md bg-slate-900 px-4 py-2 text-sm font-medium text-white"&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Jp&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"h-4 w-4"&lt;/span&gt; &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      Ship to Japan
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The label already names the country, so the icon is decorative: keep &lt;code&gt;aria-hidden="true"&lt;/code&gt; and let the text carry the meaning for screen readers.&lt;/p&gt;

&lt;h3&gt;
  
  
  List rows
&lt;/h3&gt;

&lt;p&gt;Lists are where alignment matters most, because any drift repeats down the whole column. Set one icon size for the list and align every row the same way. The visual center stays locked regardless of how wide or narrow each country's outline is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;It&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/react/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Us&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/react/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Jp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/react/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;countries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;United States&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Us&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;IT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Italy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;It&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;JP&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Japan&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Jp&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CountryList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"divide-y divide-slate-200"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;countries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Icon&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center gap-3 py-2 text-base"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Icon&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"h-5 w-5 shrink-0"&lt;/span&gt; &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;shrink-0&lt;/code&gt; utility stops the icon from squeezing when a long country name wraps, keeping the left edge of every label flush.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inputs
&lt;/h3&gt;

&lt;p&gt;For a country field, place the icon inside the input using absolute positioning and pad the text so it never overlaps the shape. Vertical centering comes from &lt;code&gt;top-1/2&lt;/code&gt; plus a &lt;code&gt;-translate-y-1/2&lt;/code&gt; nudge, which keeps the icon centered no matter the input height.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Us&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/react/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CountryInput&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"relative"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Us&lt;/span&gt;
        &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"pointer-events-none absolute left-3 top-1/2 h-5 w-5 -translate-y-1/2"&lt;/span&gt;
        &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
        &lt;span class="na"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"United States"&lt;/span&gt;
        &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-full rounded-md border border-slate-300 py-2 pl-10 pr-3 text-base"&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;pointer-events-none&lt;/code&gt; utility lets clicks pass through the icon to the field, so the input stays fully focusable and editable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inline with text
&lt;/h3&gt;

&lt;p&gt;Inside a sentence, set the icon's width and height in &lt;code&gt;em&lt;/code&gt; units so they track the surrounding font size automatically, and add a small &lt;code&gt;translate-y&lt;/code&gt; to sit it on the text baseline. The &lt;code&gt;h-[1em] w-[1em]&lt;/code&gt; classes are still the usual width and height, just expressed relative to the text instead of a fixed pixel value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;It&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/react/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;InlineMention&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-base"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      Our team just opened a new office in&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"inline-flex items-center gap-1 font-medium"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;It&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"h-[1em] w-[1em] translate-y-px"&lt;/span&gt; &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        Italy
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      .
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the width and height are &lt;code&gt;1em&lt;/code&gt;, the icon scales with the text: bump the paragraph to &lt;code&gt;text-lg&lt;/code&gt; and the icon grows with it, no extra classes needed.&lt;/p&gt;

&lt;p&gt;Every pattern here reuses the same three ideas, so once the alignment feels right in one component it stays right everywhere you drop an icon.&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>design</category>
      <category>layout</category>
      <category>alignment</category>
    </item>
    <item>
      <title>Make your icons follow dark mode automatically with currentColor</title>
      <dc:creator>Rushan</dc:creator>
      <pubDate>Thu, 16 Jul 2026 15:00:00 +0000</pubDate>
      <link>https://dev.to/rushan123/make-your-icons-follow-dark-mode-automatically-with-currentcolor-5ed5</link>
      <guid>https://dev.to/rushan123/make-your-icons-follow-dark-mode-automatically-with-currentcolor-5ed5</guid>
      <description>&lt;p&gt;Most icon sets bake a color into the SVG. The fill is set to &lt;code&gt;#111&lt;/code&gt; in the file, so the icon looks right on a white page and then disappears the moment you switch to a dark background. You end up passing a color prop to every icon, or writing theme-specific overrides for each one.&lt;/p&gt;

&lt;p&gt;GeoIcons take the other approach. Each icon draws its outline with &lt;code&gt;stroke="currentColor"&lt;/code&gt;, so it reads the CSS &lt;code&gt;color&lt;/code&gt; of whatever wraps it. Set the text color for your light and dark themes once, and every icon on the page follows along:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Jp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Fr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;De&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/react/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;EuropeanUnionEu&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/react/areas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// None of these set a color. They all inherit the text color of the wrapper.&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-slate-900 dark:text-slate-100"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Jp&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; 
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Fr&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; 
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;De&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; 
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EuropeanUnionEu&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every GeoIcon draws with &lt;code&gt;stroke="currentColor"&lt;/code&gt;, so it inherits the CSS &lt;code&gt;color&lt;/code&gt; of its parent.&lt;/li&gt;
&lt;li&gt;To support dark mode, set &lt;code&gt;color&lt;/code&gt; for each theme in CSS. You never touch the icon.&lt;/li&gt;
&lt;li&gt;A Tailwind class like &lt;code&gt;dark:text-slate-100&lt;/code&gt; works directly on the icon or on any ancestor.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What does currentColor do?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;currentColor&lt;/code&gt; is a CSS keyword that means "the computed value of this element's &lt;code&gt;color&lt;/code&gt; property." A GeoIcon sets &lt;code&gt;stroke="currentColor"&lt;/code&gt; on its &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt;, so the outline paints in whatever &lt;code&gt;color&lt;/code&gt; the icon inherits. Change the &lt;code&gt;color&lt;/code&gt; anywhere up the tree, and the stroke changes with it.&lt;/p&gt;

&lt;p&gt;That is the whole mechanism. There is no theme context, no provider, no color prop threaded through your components. The icon is a piece of text as far as color is concerned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#e11d48&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Fr&lt;/span&gt; &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; France
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;&amp;lt;Fr /&amp;gt;&lt;/code&gt; outline turns rose because it inherits &lt;code&gt;color: #e11d48&lt;/code&gt; from the paragraph.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do hardcoded icon colors break in dark mode?
&lt;/h2&gt;

&lt;p&gt;An icon with a fixed &lt;code&gt;fill="#111827"&lt;/code&gt; in its SVG cannot respond to a theme. Dark mode flips your background to near-black, and the icon stays near-black on top of it, so it vanishes. Your only fixes are to override the fill per theme or swap in a second icon file for dark backgrounds.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;currentColor&lt;/code&gt; removes that work. The icon holds no color, so there is nothing to override. You style the container, and the icon inherits the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I switch icons for a dark theme?
&lt;/h2&gt;

&lt;p&gt;Pick the color for each theme and let the icon inherit it. If you toggle dark mode with a class on &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; (the common pattern in Tailwind and most CSS setups), scope the &lt;code&gt;color&lt;/code&gt; to that class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0f172a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* slate-900 for light mode */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.dark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e2e8f0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* slate-200 for dark mode */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every icon under &lt;code&gt;&amp;lt;html class="dark"&amp;gt;&lt;/code&gt; now paints in &lt;code&gt;#e2e8f0&lt;/code&gt;. With Tailwind you can skip the CSS and put the two colors on the icon itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;De&lt;/span&gt;
  &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-slate-900 dark:text-slate-100"&lt;/span&gt;
  &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Germany"&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;text-*&lt;/code&gt; utilities set &lt;code&gt;color&lt;/code&gt;, and the icon's &lt;code&gt;currentColor&lt;/code&gt; stroke picks it up. One element, both themes.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I follow the system setting instead?
&lt;/h2&gt;

&lt;p&gt;Read the operating system preference with the &lt;code&gt;prefers-color-scheme&lt;/code&gt; media query and set &lt;code&gt;color&lt;/code&gt; from it. This needs no JavaScript and no class toggle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0f172a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e2e8f0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the icons track the user's system theme. Someone on a phone that flips to dark at sunset sees the lighter outline without any work from you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can I recolor an icon on hover?
&lt;/h2&gt;

&lt;p&gt;Yes. Because the icon follows &lt;code&gt;color&lt;/code&gt;, any state that changes &lt;code&gt;color&lt;/code&gt; also changes the icon. A hover on the button recolors the outline inside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.icon-button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#64748b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* slate-500 at rest */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.icon-button&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#2563eb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* blue-600 on hover */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"icon-button"&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Filter by France"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Fr&lt;/span&gt; &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same idea covers focus rings and disabled states. You style the button, and the icon comes along.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outline or solid: stroke versus fill
&lt;/h2&gt;

&lt;p&gt;GeoIcons render as outlines. The &lt;code&gt;stroke&lt;/code&gt; is &lt;code&gt;currentColor&lt;/code&gt; and the &lt;code&gt;fill&lt;/code&gt; is &lt;code&gt;none&lt;/code&gt;, which is why theming runs through the stroke. If you want a solid glyph, set &lt;code&gt;fill&lt;/code&gt; yourself and keep it on &lt;code&gt;currentColor&lt;/code&gt; so it still follows the theme:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Fr&lt;/span&gt; &lt;span class="na"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt; &lt;span class="na"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"France"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both the stroke path and a &lt;code&gt;currentColor&lt;/code&gt; fill respect the same inherited &lt;code&gt;color&lt;/code&gt;, so a solid icon themes exactly like an outline one.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do I make an icon change color in dark mode?
&lt;/h3&gt;

&lt;p&gt;Set the CSS color property for your dark theme, either on a .dark class or through a prefers-color-scheme media query. GeoIcons draw with stroke='currentColor', so each icon inherits that color automatically. You do not pass a color prop to the icon itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need a color prop on every GeoIcon?
&lt;/h3&gt;

&lt;p&gt;No. Each icon borrows the CSS color of its container through currentColor. Set color once per theme and every icon follows. Pass an explicit stroke or fill only when a single icon needs to break from the surrounding text color.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I give one icon a different color from the surrounding text?
&lt;/h3&gt;

&lt;p&gt;Yes. Pass an explicit stroke (or fill) on that single icon, such as stroke='#2563eb', or wrap it in an element that sets its own color. currentColor only applies when you leave the color unset, so an override on one icon does not touch the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get started
&lt;/h2&gt;

&lt;p&gt;Install the package and let your theme drive the color:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @geoicons/react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Jp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/react/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-slate-900 dark:text-slate-100"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Jp&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Japan"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For every prop each icon accepts, see &lt;a href="https://geoicons.io/docs/api" rel="noopener noreferrer"&gt;the GeoIcons API reference&lt;/a&gt;. To learn why importing icons this way keeps your bundle small, read &lt;a href="https://geoicons.io/blog/tree-shaking-icons" rel="noopener noreferrer"&gt;why an icon library doesn't have to bloat your bundle&lt;/a&gt;. New here? Start with &lt;a href="https://geoicons.io/blog/add-country-icons-react" rel="noopener noreferrer"&gt;adding your first country icon&lt;/a&gt; or &lt;a href="https://geoicons.io/icons/countries" rel="noopener noreferrer"&gt;browse all 422 icons&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>darkmode</category>
      <category>currentcolor</category>
      <category>svgicons</category>
      <category>theming</category>
    </item>
    <item>
      <title>Introducing GeoIcons: 422 tree-shakable geographic icons</title>
      <dc:creator>Rushan</dc:creator>
      <pubDate>Wed, 15 Jul 2026 06:27:44 +0000</pubDate>
      <link>https://dev.to/rushan123/introducing-geoicons-422-tree-shakable-geographic-icons-43i</link>
      <guid>https://dev.to/rushan123/introducing-geoicons-422-tree-shakable-geographic-icons-43i</guid>
      <description>&lt;p&gt;GeoIcons is a library of geographic map icons for the web: &lt;strong&gt;255 countries and 167 areas&lt;/strong&gt;, 422 icons in total at the time of this post, built for React, Vue, Angular, and vanilla JS. More is on the way, including subdivisions and flags. Every icon is a named export, so your bundler keeps only the ones you import. Here are three of them, inheriting the current text color:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;422 icons: 255 countries (ISO 3166) + 167 areas (regions, continents, landforms, subdivisions).&lt;/li&gt;
&lt;li&gt;Tree-shakable by design. One import ships one icon, not the catalog.&lt;/li&gt;
&lt;li&gt;Themed with &lt;code&gt;currentColor&lt;/code&gt; and an adjustable &lt;code&gt;strokeWidth&lt;/code&gt;. Accessible out of the box.&lt;/li&gt;
&lt;li&gt;React, Vue, Angular, and vanilla JS. Free under GPLv3, commercial license available.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What ships in the box
&lt;/h2&gt;

&lt;p&gt;The set covers &lt;strong&gt;255 country icons&lt;/strong&gt;, named by their ISO 3166 alpha-2 code, and &lt;strong&gt;167 area icons&lt;/strong&gt; for regions, continents, landforms, and subdivisions. Enough to build a country picker or label a map legend without a second asset library.&lt;/p&gt;

&lt;p&gt;Every icon is a single SVG component. No sprite sheet to host and no font to load.&lt;/p&gt;

&lt;h2&gt;
  
  
  One import ships one icon
&lt;/h2&gt;

&lt;p&gt;GeoIcons has no central registry and no &lt;code&gt;&amp;lt;Icon name="us" /&amp;gt;&lt;/code&gt; string API. Each icon is its own named export, which is what lets bundlers drop everything you never reference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Us&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Fr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Jp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/react/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Legend&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Us&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"United States"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; United States&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Fr&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"France"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; France&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Jp&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Japan"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; Japan&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Import three icons and your build includes three icons. The other 419 never touch your bundle. For the mechanics behind that, read &lt;a href="https://geoicons.io/blog/tree-shaking-icons" rel="noopener noreferrer"&gt;why an icon library doesn't have to bloat your bundle&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Themeable with currentColor
&lt;/h2&gt;

&lt;p&gt;Icons paint with &lt;code&gt;currentColor&lt;/code&gt;, so they follow the text color of whatever wraps them. Change weight with &lt;code&gt;strokeWidth&lt;/code&gt;, or fill them solid. No CSS overrides, no per-icon color props to thread through your components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Default, colored, heavier stroke, filled&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Ng&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Ng&lt;/span&gt; &lt;span class="na"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"#2563eb"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Ng&lt;/span&gt; &lt;span class="na"&gt;strokeWidth&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Ng&lt;/span&gt; &lt;span class="na"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because color is inherited, the same icon works in light mode, dark mode, and on a colored button without any extra styling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessible by default
&lt;/h2&gt;

&lt;p&gt;Icons are decorative unless you say otherwise. Add a label and the icon becomes meaningful to screen readers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Us&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"United States"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood each icon namespaces its &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; id with React's &lt;code&gt;useId()&lt;/code&gt;, so rendering the same icon twice on a page never produces duplicate ids.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four frameworks, one icon set
&lt;/h2&gt;

&lt;p&gt;The same icons ship for every major framework, imported the same way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Us&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/react/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// React&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Us&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/vue/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// Vue&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Us&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/angular/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Angular&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Us&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/vanilla/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Vanilla JS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@geoicons/core&lt;/code&gt; holds the shared pieces the framework packages build on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Naming you can guess
&lt;/h2&gt;

&lt;p&gt;Countries use their ISO 3166 alpha-2 code in PascalCase: &lt;code&gt;Us&lt;/code&gt;, &lt;code&gt;Fr&lt;/code&gt;, &lt;code&gt;Jp&lt;/code&gt;, &lt;code&gt;De&lt;/code&gt;, &lt;code&gt;Br&lt;/code&gt;. Areas use a slug in PascalCase, such as &lt;code&gt;EuropeanUnionEu&lt;/code&gt;. If you know the country code, you know the import name, so autocomplete does most of the work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Licensing
&lt;/h2&gt;

&lt;p&gt;GeoIcons is dual-licensed. It is &lt;strong&gt;free under GPLv3&lt;/strong&gt; for open-source and GPL-compatible projects, and a &lt;strong&gt;commercial license&lt;/strong&gt; covers everything else. Icons render the same either way. An unlicensed commercial use logs an offline console warning and nothing else, so nothing breaks at runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get started
&lt;/h2&gt;

&lt;p&gt;Install the package for your framework and import your first icon:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @geoicons/react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Us&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@geoicons/react/countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Us&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"United States"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full API, styling, and accessibility docs live in &lt;a href="https://geoicons.io/docs/api" rel="noopener noreferrer"&gt;the docs&lt;/a&gt;, or &lt;a href="https://geoicons.io/icons/countries" rel="noopener noreferrer"&gt;browse all 422 icons&lt;/a&gt; to find what you need.&lt;/p&gt;

</description>
      <category>announcement</category>
      <category>geoicons</category>
      <category>react</category>
      <category>svgicons</category>
    </item>
  </channel>
</rss>
