<?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>How to add country icons to a React app</title>
      <dc:creator>Rushan</dc:creator>
      <pubDate>Mon, 31 Aug 2026 10:09:16 +0000</pubDate>
      <link>https://dev.to/rushan123/how-to-add-country-icons-to-a-react-app-185</link>
      <guid>https://dev.to/rushan123/how-to-add-country-icons-to-a-react-app-185</guid>
      <description>&lt;p&gt;Geography is a blind spot in most icon libraries. They give you arrows and carets, not the shape of Japan or the outline of the EU, and the geographic packs that exist rarely fit a modern React bundle. GeoIcons does: 799 country, area, and subdivision icons, each its own named export. Adding one to a React app takes three steps: install the package, import the icon by its ISO code, and render it:&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;UnitedStates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;France&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Japan&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;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UnitedStates&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 States"&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;France&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;"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;Japan&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;"Japan"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&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;blockquote&gt;
&lt;p&gt;&lt;a href="https://geoicons.io/blog/add-country-icons-react" rel="noopener noreferrer"&gt;See it live on geoicons.io →&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is the whole path. Below: sizing, color, accessibility, and choosing an icon when you only know the country at runtime.&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;Install &lt;code&gt;@geoicons/react&lt;/code&gt;, then import each country by its 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;/li&gt;
&lt;li&gt;Icons are React components. Size them with &lt;code&gt;size&lt;/code&gt;, color them with &lt;code&gt;currentColor&lt;/code&gt;, adjust weight with &lt;code&gt;strokeWidth&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;aria-label&lt;/code&gt; when the icon carries meaning; leave it off (or &lt;code&gt;aria-hidden&lt;/code&gt;) when it is decorative.&lt;/li&gt;
&lt;li&gt;For a country chosen at runtime, build a small map of only the icons you import so the bundle stays tree-shakable.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 1: Install the React package
&lt;/h2&gt;

&lt;p&gt;Add the package to your project:&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;p&gt;It has no runtime dependencies beyond React and ships as &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules" rel="noopener noreferrer"&gt;ES modules&lt;/a&gt;, so your bundler can tree-shake it. Tree-shaking is dead-code elimination: the bundler reads your static &lt;code&gt;import&lt;/code&gt; statements at build time, keeps only the icons you name, and drops the rest. See &lt;a href="https://geoicons.io/blog/tree-shaking-icons" rel="noopener noreferrer"&gt;how it keeps the bundle small&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Import by ISO code
&lt;/h2&gt;

&lt;p&gt;Each country is a named export keyed to its &lt;a href="https://www.iso.org/iso-3166-country-codes.html" rel="noopener noreferrer"&gt;ISO 3166 alpha-2 code&lt;/a&gt;, PascalCased. If you know the code, you know the import name, so autocomplete does most of the work:&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;UnitedStates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;France&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Japan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Germany&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Brazil&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Us&lt;/code&gt; is the United States, &lt;code&gt;Fr&lt;/code&gt; is France, &lt;code&gt;De&lt;/code&gt; is Germany, &lt;code&gt;Br&lt;/code&gt; is Brazil. Importing from the &lt;code&gt;/countries&lt;/code&gt; subpath keeps the module graph tight: you reach into the countries entry point instead of a root barrel that touches every category.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Render and style
&lt;/h2&gt;

&lt;p&gt;Each import is a plain React component. Drop it into your JSX and control it with props:&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;// Size in pixels, color through currentColor, weight through strokeWidth&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Germany&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;Germany&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;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;Germany&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;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;Germany&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;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;Every icon takes the same props:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prop&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;size&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Width and height in pixels (number or string).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stroke&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Outline color. Defaults to &lt;code&gt;currentColor&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;strokeWidth&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Outline thickness.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fill color. Set to &lt;code&gt;currentColor&lt;/code&gt; for a solid icon.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aria-label&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Accessible name. Omit for a decorative icon.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The same four, live:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://geoicons.io/blog/add-country-icons-react" rel="noopener noreferrer"&gt;See it live on geoicons.io →&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Color comes from &lt;code&gt;currentColor&lt;/code&gt; by default, so an icon follows the text color of whatever wraps it. Put one in a dark navbar and it turns light; put it on a colored button and it matches, with no per-icon color prop to thread through your tree.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make it accessible
&lt;/h2&gt;

&lt;p&gt;An icon is decorative until you give it a name. If the icon repeats a label that is already in the text, leave it decorative so screen readers skip 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="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;UnitedStates&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; 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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the icon is the only thing conveying the country, give it an &lt;code&gt;aria-label&lt;/code&gt; so assistive tech can announce 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="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;"Select United States"&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;UnitedStates&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;Under the hood each icon namespaces its &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; id with 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;, so rendering the same country twice on one page never produces duplicate ids.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I render a country icon at runtime?
&lt;/h2&gt;

&lt;p&gt;A country picker or a data table often knows the country only as a string at render time. We left GeoIcons without an &lt;code&gt;&amp;lt;Icon name="us" /&amp;gt;&lt;/code&gt; lookup on purpose, because a string API forces the library to keep every icon. Build your own small map instead, listing only the countries you import:&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;UnitedStates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;France&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Japan&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;// You own this map, so it holds only the three icons you imported.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;byCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;us&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UnitedStates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;France&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;jp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Japan&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&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;CountryIcon&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="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;byCode&lt;/span&gt; &lt;span class="p"&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;Icon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;byCode&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="k"&gt;return&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;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;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bundler still sees three static imports at the top and prunes the other 419. You get a runtime lookup where you need one, without dragging in the whole catalog. For why the string-prop API breaks this, see &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;
  
  
  Beyond countries: areas
&lt;/h2&gt;

&lt;p&gt;The same import pattern covers 167 area icons: regions, continents, landforms, and groupings like the European Union. Areas use a PascalCase slug rather than an ISO code:&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;EuropeanUnion&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;EuropeanUnion&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;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do I add a country icon to a React app?
&lt;/h3&gt;

&lt;p&gt;Install &lt;a class="mentioned-user" href="https://dev.to/geoicons"&gt;@geoicons&lt;/a&gt;/react, import the country by its ISO 3166 alpha-2 code in PascalCase, and render it as a component. For example, import { UnitedStates } from '&lt;a class="mentioned-user" href="https://dev.to/geoicons"&gt;@geoicons&lt;/a&gt;/react/countries' gives you a  component you can size and color like any SVG.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I show an icon for a country chosen at runtime?
&lt;/h3&gt;

&lt;p&gt;Import the countries you need and build a small object mapping codes to components, such as { us: UnitedStates, fr: France }. Look the component up by key at render time. This keeps the API tree-shakable because you only import the icons the map lists.&lt;/p&gt;

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

&lt;p&gt;Install the package and render your first country:&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;UnitedStates&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;UnitedStates&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;The full prop and import reference lives in &lt;a href="https://geoicons.io/docs/api" rel="noopener noreferrer"&gt;the GeoIcons API reference&lt;/a&gt;, or &lt;a href="https://geoicons.io/icons/countries" rel="noopener noreferrer"&gt;browse all 799 icons&lt;/a&gt; to find the ones you need. New to the library? Start with &lt;a href="https://geoicons.io/blog/introducing-geoicons" rel="noopener noreferrer"&gt;the launch post&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>countryicons</category>
      <category>svgicons</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>SVG icons in React Server Components: what ships to the client</title>
      <dc:creator>Rushan</dc:creator>
      <pubDate>Mon, 31 Aug 2026 10:07:54 +0000</pubDate>
      <link>https://dev.to/rushan123/svg-icons-in-react-server-components-what-ships-to-the-client-327e</link>
      <guid>https://dev.to/rushan123/svg-icons-in-react-server-components-what-ships-to-the-client-327e</guid>
      <description>&lt;p&gt;The App Router flipped the default. Every component you write is a Server Component until something opts it out, and one &lt;code&gt;'use client'&lt;/code&gt; at the top of a file opts out everything below it. That makes a question worth asking before you add an icon library: does importing a single icon pull your page into a client tree?&lt;/p&gt;

&lt;p&gt;For a lot of libraries the answer is yes. Nothing breaks when it happens, the page still renders, and you start shipping JavaScript for a static drawing.&lt;/p&gt;

&lt;p&gt;GeoIcons icon components carry no &lt;code&gt;'use client'&lt;/code&gt; directive, so they render on the server and arrive as plain markup. Checking any library for this takes one search. Setting it up and confirming the result takes four steps.&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;// app/page.tsx (no 'use client' anywhere)&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;UnitedStates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;France&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Japan&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;Page&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;"flex items-center gap-4"&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;UnitedStates&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 States"&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;France&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;"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;Japan&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;"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;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;blockquote&gt;
&lt;p&gt;&lt;a href="https://geoicons.io/blog/svg-icons-react-server-components" rel="noopener noreferrer"&gt;See it live on geoicons.io →&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That page is a Server Component. Next.js generates the SVG markup at build time and serves it as HTML, so no icon code runs in the browser. The route still loads React itself, and the RSC payload repeats the same path data further down the document, which matters when you go counting tags in Step 4.&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;A &lt;code&gt;'use client'&lt;/code&gt; directive inside an icon library turns every page that imports from it into a client tree. Check for it before you install.&lt;/li&gt;
&lt;li&gt;GeoIcons icon components have no directive, so you can import and render them inside a Server Component.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IconProvider&lt;/code&gt; is the one client piece, because it needs state and effects. Icons never read its context, so it can sit in your root layout and nothing else has to move.&lt;/li&gt;
&lt;li&gt;Search the built client chunks to confirm. If no icon path string lands in one, the library cost you no client JavaScript.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 1: Find out whether your icon library is a Client Component
&lt;/h2&gt;

&lt;p&gt;The directive has to be the first statement in a module, so you can settle this with one search over the installed package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rl&lt;/span&gt; &lt;span class="s2"&gt;"^['&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;]use client['&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;]"&lt;/span&gt; node_modules/@geoicons/react/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For GeoIcons that returns a single file, &lt;code&gt;dist/_license.js&lt;/code&gt;, which is the license provider. None of the icon modules appear, because none of them declare the directive.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;^&lt;/code&gt; anchor earns its place. A directive only counts as the first statement in a module, and the GeoIcons icon source happens to mention &lt;code&gt;use client&lt;/code&gt; inside a comment explaining why it avoids one. Drop the anchor and the search matches all 799 icon files while telling you nothing.&lt;/p&gt;

&lt;p&gt;Run the same search against your current icon library. If every icon file comes back, importing one icon marks that module as a client boundary, and every component you render underneath it becomes client code too.&lt;/p&gt;

&lt;p&gt;You can also open the file. A GeoIcons country component is short enough to read at a glance:&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;// node_modules/@geoicons/react/countries/base/Nz.tsx (abridged)&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;useId&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;react&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="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SVGProps&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;react&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;noteIconRender&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/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;SVGProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SVGSVGElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;strokeWidth&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Nz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;strokeWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&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="nx"&gt;ariaLabel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;Props&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="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="nf"&gt;noteIconRender&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;svg&lt;/span&gt;
      &lt;span class="na"&gt;viewBox&lt;/span&gt;&lt;span class="p"&gt;=&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="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="si"&gt;}&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;"currentColor"&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="nx"&gt;strokeWidth&lt;/span&gt;&lt;span class="si"&gt;}&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;"none"&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ariaLabel&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;img&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;aria-labelledby&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="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="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ariaLabel&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="kc"&gt;true&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&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="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;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt; &lt;span class="na"&gt;d&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"M7.646 22.656 4.694 21.39a.5.5 0 0 1-.132-.836l5.414-4.75…"&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;svg&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;Files are named by ISO alpha-2 code, and the barrel re-exports each one under its full country name as well, which is why you import &lt;code&gt;NewZealand&lt;/code&gt; and read &lt;code&gt;Nz.tsx&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Everything in there runs on the server. &lt;code&gt;useId&lt;/code&gt; derives an identifier from the component's position in the render tree, which is why it works during server rendering. The icon spends it twice, as the &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; element's &lt;code&gt;id&lt;/code&gt; and as the &lt;code&gt;aria-labelledby&lt;/code&gt; value on the &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt;, and that pair is what lets a screen reader read the label. Pass no &lt;code&gt;aria-label&lt;/code&gt; and the icon sets &lt;code&gt;aria-hidden&lt;/code&gt; on itself instead, so a decorative icon stays out of the accessibility tree without you asking. &lt;a href="https://geoicons.io/blog/svg-icons-screen-reader-accessible" rel="noopener noreferrer"&gt;Making SVG icons screen-reader accessible&lt;/a&gt; goes further into that behavior.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;noteIconRender()&lt;/code&gt; call is a license compliance nudge. GeoIcons writes it as a plain guarded function so it does not need &lt;code&gt;useEffect&lt;/code&gt;, because reaching for an effect there would have forced the directive onto all 799 icons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Render icons directly in a Server Component
&lt;/h2&gt;

&lt;p&gt;Once you know the icons are server-safe, import and render them:&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;// app/countries/page.tsx&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;UnitedStates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Germany&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Japan&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;rows&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;Icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UnitedStates&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="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;Germany&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;Germany&lt;/span&gt;&lt;span class="dl"&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;Icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Japan&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="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;CountriesPage&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"&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;rows&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;Icon&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="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;name&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 px-3 py-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;Icon&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-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;blockquote&gt;
&lt;p&gt;&lt;a href="https://geoicons.io/blog/svg-icons-react-server-components" rel="noopener noreferrer"&gt;See it live on geoicons.io →&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The explicit &lt;code&gt;aria-hidden&lt;/code&gt; there is belt and braces. Each row already names its country in text, and an icon with no &lt;code&gt;aria-label&lt;/code&gt; hides itself, so you could leave it off and get the same result.&lt;/p&gt;

&lt;p&gt;Passing components around in an array works here because the page renders on the server and the array never reaches the browser. The icons still tree-shake, because each one is its own named export and the bundler can see which three you referenced. &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; covers that mechanism.&lt;/p&gt;

&lt;p&gt;Icons take their stroke color from CSS through &lt;code&gt;currentColor&lt;/code&gt;, so a row in a dark panel gets a light icon without a color prop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Keep the provider at a client boundary
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;IconProvider&lt;/code&gt; is the exception. It reads a license key, verifies it, and stores the result in context. That takes state and effects, so it declares &lt;code&gt;'use client'&lt;/code&gt;. Only that one module is client code, and the icons never read its context to render.&lt;/p&gt;

&lt;p&gt;Put it once in the root layout:&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;// app/layout.tsx&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;IconProvider&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&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;RootLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&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;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"en"&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;body&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;IconProvider&lt;/span&gt; &lt;span class="na"&gt;licenseKey&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_GEOICONS_KEY&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;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IconProvider&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;body&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;html&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;A Client Component can render Server Components handed to it as &lt;code&gt;children&lt;/code&gt;, so wrapping the tree this way leaves your pages on the server.&lt;/p&gt;

&lt;p&gt;If your project is open source under GPLv3, pass the &lt;code&gt;GPL_DECLARATION&lt;/code&gt; constant instead of a key. &lt;code&gt;@geoicons/react&lt;/code&gt; re-exports it, so you can import it alongside the provider. If you are still evaluating, leave the provider out. Icons render normally either way, and the &lt;a href="https://geoicons.io/docs/commercial-license" rel="noopener noreferrer"&gt;commercial license setup&lt;/a&gt; has the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Verify what reached the browser
&lt;/h2&gt;

&lt;p&gt;Do not take the setup on trust. Build the app and read the HTML you serve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx next build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npx next start
curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://localhost:3000/countries | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;svg"&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;grep -o&lt;/code&gt; here rather than &lt;code&gt;grep -c&lt;/code&gt;. Next.js emits its HTML as a handful of very long lines, and &lt;code&gt;-c&lt;/code&gt; counts matching lines, so it reports &lt;code&gt;1&lt;/code&gt; however many icons the page holds. Expect each icon to turn up twice in the count, once in the markup and once in the RSC payload.&lt;/p&gt;

&lt;p&gt;Seeing real &lt;code&gt;&amp;lt;path d="M…"&amp;gt;&lt;/code&gt; data in that response tells you the server drew the icon. A client-rendered library will still show markup here thanks to SSR, so settle the question in the build output instead. Take a path string you know belongs to an icon and look for it in the client chunks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rl&lt;/span&gt; &lt;span class="s2"&gt;"M7.646 22.656"&lt;/span&gt; .next/static/chunks/ &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"not in any client chunk"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing found means nothing shipped. A hit does not prove the opposite. It means something in your app renders that icon inside a client boundary, which is a decision you made rather than one the library forced on you. Reading the network tab is the less decisive move, because Next.js shares chunks across routes and one library's cost rarely separates out cleanly.&lt;/p&gt;

&lt;p&gt;This site is the working example, and it shows both sides. The related-icons grid at the foot of any icon page is a plain Server Component rendering icons directly, and it arrives in the prerendered HTML with full path data. Run the chunk search against the site and you will still find country paths, because the studio panel higher up the same page is a Client Component that renders icons on demand. Read the grid as the proof rather than the whole page.&lt;/p&gt;

&lt;h2&gt;
  
  
  When you do need "use client"
&lt;/h2&gt;

&lt;p&gt;Server rendering covers display. When an icon has to respond to a click or a hover, put that behavior in a wrapper:&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use client&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;useState&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;react&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;France&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;SelectableCountry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSelected&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&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;onClick&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setSelected&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&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="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;selected&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-blue-600&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;text-gray-500&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;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;France&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;"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="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 icon did not change. It is the same component from the same import, rendering inside a client boundary this time. The icons work on both sides, so you place the boundary where your interface needs it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do GeoIcons work in React Server Components?
&lt;/h3&gt;

&lt;p&gt;Yes. The icon components carry no 'use client' directive, so they render inside Server Components and arrive as HTML with no icon code running in the browser. Only IconProvider, the license wrapper, is a Client Component, and the icons never read its context to render.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why can an icon component call useId and still be a Server Component?
&lt;/h3&gt;

&lt;p&gt;useId derives an identifier from the component's position in the render tree rather than from state or effects, which is why it works during server rendering. The icon uses it as the title element's id and as the aria-labelledby value on the svg, which is the pair a screen reader needs to read the label you pass as aria-label.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I check whether my current icon library forces a client boundary?
&lt;/h3&gt;

&lt;p&gt;Search the installed package for a use client directive anchored to the start of a line, for example with grep -rl over node_modules for your icon package. Anchor it: a directive only counts as the first statement in a module, and an unanchored search will also match the string inside comments. If the icon modules come back in the results, importing an icon marks that module as a client boundary and everything rendered underneath it becomes client code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does wrapping my app in IconProvider make every page a Client Component?
&lt;/h3&gt;

&lt;p&gt;No. A Client Component can render Server Components handed to it as children, so a provider in the root layout leaves the pages beneath it on the server. Only the provider module itself is client code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I use the same icon in both a Server Component and a Client Component?
&lt;/h3&gt;

&lt;p&gt;Yes, and it is the same import either way. Render it on the server for display, or render it inside a component marked 'use client' when it needs a click handler, hover state, or animation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this leaves you
&lt;/h2&gt;

&lt;p&gt;Icons are static artwork. In an App Router app they should cost you markup and nothing more, and whether they do comes down to one directive in the library you picked.&lt;/p&gt;

&lt;p&gt;Check for it before you install. If the icons are server-safe, render them directly, keep any provider at the root, and search the client chunks to confirm.&lt;/p&gt;

&lt;p&gt;The full prop list lives in the &lt;a href="https://geoicons.io/docs/api/react" rel="noopener noreferrer"&gt;React API reference&lt;/a&gt;, and &lt;a href="https://geoicons.io/blog/add-country-icons-react" rel="noopener noreferrer"&gt;adding country icons to a React app&lt;/a&gt; covers sizing, color, and accessibility from the start.&lt;/p&gt;

</description>
      <category>react</category>
      <category>nextjs</category>
      <category>servercomponents</category>
      <category>svgicons</category>
    </item>
    <item>
      <title>Why an icon library doesn't have to bloat your bundle</title>
      <dc:creator>Rushan</dc:creator>
      <pubDate>Mon, 31 Aug 2026 10:06:44 +0000</pubDate>
      <link>https://dev.to/rushan123/why-an-icon-library-doesnt-have-to-bloat-your-bundle-55na</link>
      <guid>https://dev.to/rushan123/why-an-icon-library-doesnt-have-to-bloat-your-bundle-55na</guid>
      <description>&lt;p&gt;Tree-shaking is why a GeoIcons install can hold 799 icons, the count at the time of this post and still growing, while your app ships three. When you write:&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;your bundler includes the &lt;code&gt;Us&lt;/code&gt; component and drops the rest. You pay for what you import, not for what the package contains.&lt;/p&gt;

&lt;p&gt;That sentence hides a lot of moving parts. Bundlers only drop code when the library is built to let them, and icon libraries are one of the easiest places to get it wrong.&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;Tree-shaking is dead-code elimination driven by static analysis of ES module imports.&lt;/li&gt;
&lt;li&gt;It works only when a library uses named exports, ships ES modules, and marks itself side-effect free.&lt;/li&gt;
&lt;li&gt;A central registry or &lt;code&gt;&amp;lt;Icon name="us" /&amp;gt;&lt;/code&gt; lookup defeats it: the bundler cannot tell which icons you use, so it keeps all of them.&lt;/li&gt;
&lt;li&gt;GeoIcons gives every icon its own named export and no runtime registry, so unused icons never reach your bundle.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What tree-shaking does
&lt;/h2&gt;

&lt;p&gt;Tree-shaking is dead-code elimination for JavaScript modules. Bundlers like webpack, Rollup, esbuild, and Vite read the static &lt;code&gt;import&lt;/code&gt; and &lt;code&gt;export&lt;/code&gt; statements in your code, build a graph of what actually gets used, and leave everything else out of the final bundle.&lt;/p&gt;

&lt;p&gt;The word "static" is the load-bearing part. ES modules declare their imports at the top level with literal names, so a bundler can decide at build time which exports are reachable without running your code. Here you reach three exports:&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bundler follows those edges to the &lt;code&gt;Us&lt;/code&gt;, &lt;code&gt;Fr&lt;/code&gt;, and &lt;code&gt;Jp&lt;/code&gt; components, marks them as used, and never marks the other 419. Unmarked exports are unreachable, so they get pruned. Three imports in, three components in your bundle. The icons themselves render like this:&lt;/p&gt;

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

&lt;p&gt;Two conditions have to hold for this to work. The library must ship real ES modules, not a single CommonJS blob, because &lt;code&gt;require()&lt;/code&gt; is dynamic and cannot be statically analyzed. And the library must promise it has no import-time side effects, so the bundler knows that dropping an unused module changes nothing. Miss either one and the bundler plays it safe by keeping code it cannot prove is dead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why icon libraries are where it breaks
&lt;/h2&gt;

&lt;p&gt;Icon sets are large by nature: hundreds or thousands of small components that most apps use a handful of. That makes them the highest-leverage place for tree-shaking, and also the easiest place to accidentally turn it off.&lt;/p&gt;

&lt;p&gt;The usual culprit is a convenience API. A library exposes a single component and a string prop:&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;// The convenient-looking API that ships everything&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;Icon&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;some-icons&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;Icon&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"us"&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;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"fr"&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 &lt;code&gt;name&lt;/code&gt; prop has to resolve to a component at runtime, which means the library keeps a lookup table somewhere:&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;// Inside the library&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;flags&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;./all-flags&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;registry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;us&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;flags&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="na"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;flags&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="na"&gt;jp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Jp&lt;/span&gt; &lt;span class="cm"&gt;/* ...419 more */&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;Icon&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Svg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;registry&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Svg&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;The bundler cannot know that your app only passes &lt;code&gt;"us"&lt;/code&gt; and &lt;code&gt;"fr"&lt;/code&gt;. The value could come from a variable, a prop, or an API response. To keep the code correct, it has to retain every icon the registry can reach. You imported one component and shipped all 799.&lt;/p&gt;

&lt;p&gt;The same failure hides in a few other shapes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A barrel file that re-exports everything and is imported as a namespace: &lt;code&gt;import * as Icons from 'some-icons'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A dynamic member access: &lt;code&gt;Icons[name]&lt;/code&gt;, where &lt;code&gt;name&lt;/code&gt; is not a literal.&lt;/li&gt;
&lt;li&gt;A package published only as CommonJS, so &lt;code&gt;import&lt;/code&gt; cannot be analyzed at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each one asks the bundler a question it cannot answer at build time, and the safe answer is always "keep it."&lt;/p&gt;

&lt;h2&gt;
  
  
  How GeoIcons is built to stay shakable
&lt;/h2&gt;

&lt;p&gt;GeoIcons has no &lt;code&gt;&amp;lt;Icon name="..."&amp;gt;&lt;/code&gt; API and no central registry. Each icon is its own named export in an ES module, imported by a name your bundler can read literally:&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no lookup table for the bundler to get stuck on. The reference to &lt;code&gt;Us&lt;/code&gt; is a static edge to exactly one component, so everything you never name is unreachable and gets pruned. This is also why the library has no string-keyed API: adding one would reintroduce the exact registry that breaks pruning.&lt;/p&gt;

&lt;p&gt;The package ships ES modules and marks itself side-effect free, so bundlers are allowed to drop unused icon modules instead of keeping them just in case. Category subpaths like &lt;code&gt;@geoicons/react/countries&lt;/code&gt; keep the graph even tighter: you reach into the countries entry point rather than a root barrel that touches every category at once.&lt;/p&gt;

&lt;p&gt;The tradeoff is explicit imports. There is no &lt;code&gt;&amp;lt;Icon name={dynamicValue} /&amp;gt;&lt;/code&gt; escape hatch, so a dynamic icon whose identity is known only at runtime needs you to build your own small map of just the icons you use:&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="c1"&gt;// You control this map, so it lists only what you import.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;byCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;us&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="na"&gt;fr&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="na"&gt;jp&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;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CountryFlag&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="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;byCode&lt;/span&gt; &lt;span class="p"&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;Icon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;byCode&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="k"&gt;return&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;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;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That map holds three components because you wrote three. The bundler still sees three static imports at the top and prunes the rest. You get a runtime lookup where you need one, without dragging in the whole catalog.&lt;/p&gt;

&lt;h2&gt;
  
  
  See it in your own bundle
&lt;/h2&gt;

&lt;p&gt;Do not take the claim on faith. Measure it. Add a bundle analyzer and look at what actually ships.&lt;/p&gt;

&lt;p&gt;For a Next.js app:&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 &lt;span class="nt"&gt;-D&lt;/span&gt; @next/bundle-analyzer
&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="c1"&gt;// next.config.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;withBundleAnalyzer&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;@next/bundle-analyzer&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;analyzer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;withBundleAnalyzer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ANALYZE&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&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="nf"&gt;analyzer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="cm"&gt;/* your config */&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 shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;ANALYZE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a Vite app, &lt;code&gt;rollup-plugin-visualizer&lt;/code&gt; gives you the same treemap. Import three icons, build, and confirm that only three icon modules appear in the report. Then import a fourth and watch exactly one module join them. That linear growth, one module per import, is the signal that tree-shaking is working. If instead the whole icon set shows up after importing one, something in the chain (a barrel, a registry, a CommonJS build) has defeated it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ways to accidentally defeat it
&lt;/h2&gt;

&lt;p&gt;Even with a shakable library, your own code can force the bundler to keep more than you use. The common mistakes and their fixes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Namespace imports.&lt;/strong&gt; &lt;code&gt;import * as Icons from '@geoicons/react/countries'&lt;/code&gt; then &lt;code&gt;Icons.Us&lt;/code&gt; pulls the namespace object, which references every export. Import the named members you need instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic access on the whole set.&lt;/strong&gt; &lt;code&gt;Icons[code]&lt;/code&gt; over a namespace import is the registry antipattern in your own app. Build a small explicit map of only the icons you use, as shown above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-export barrels you own.&lt;/strong&gt; A local &lt;code&gt;icons.ts&lt;/code&gt; that re-exports everything and gets imported wholesale can undo the library's work. Import from the source subpath directly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are exotic. They creep in through a shared helper or a "cleaner" import, and a bundle analyzer catches them in one build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the bytes matter
&lt;/h2&gt;

&lt;p&gt;Every kilobyte of JavaScript is paid for three times. It travels over the network, it gets parsed and compiled by the engine, and it runs. Parse and compile time scales with the amount of script, and on a mid-range phone that cost is far higher than on a developer laptop. Shipping 400 icons a user never sees delays the moment the page becomes interactive, for no visible benefit.&lt;/p&gt;

&lt;p&gt;Tree-shaking removes that tax without asking you to manage it by hand. You do not maintain a manual subset of the library or split it into micro-packages. You import the icon you need at the point you need it, and the build keeps the bundle proportional to your actual usage. A library of 799 icons costs the same as a library of 12 if you only import 12. GeoIcons is built on that principle. &lt;a href="https://geoicons.io/icons/countries" rel="noopener noreferrer"&gt;Browse the full set&lt;/a&gt; to see what you would import.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Does importing one icon include all 799?
&lt;/h3&gt;

&lt;p&gt;No. Each icon is a separate named export, so a bundler that supports tree-shaking includes only the icons you import and drops the rest. Importing one component adds one module to your bundle, and the icons you never reference never ship.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does an &amp;lt;Icon name=\
&lt;/h3&gt;

&lt;p&gt;A name prop resolves to a component at runtime through a lookup table that references every icon. The bundler cannot tell which names you pass, so it keeps all of them. GeoIcons instead exposes each icon as its own import, which stays shakable.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I confirm tree-shaking works in my app?
&lt;/h3&gt;

&lt;p&gt;Run a bundle analyzer such as &lt;a class="mentioned-user" href="https://dev.to/next"&gt;@next&lt;/a&gt;/bundle-analyzer or rollup-plugin-visualizer, then build. Import three icons and check that only three icon modules appear in the report. Import a fourth and exactly one module should join them.&lt;/p&gt;

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

&lt;p&gt;Install the package and import your first icon. Then run the analyzer and confirm the bundle only grows by what you use:&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;The full prop and import reference lives in &lt;a href="https://geoicons.io/docs/api" rel="noopener noreferrer"&gt;the docs&lt;/a&gt;. New to the library? Start with &lt;a href="https://geoicons.io/blog/introducing-geoicons" rel="noopener noreferrer"&gt;the launch post&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>treeshaking</category>
      <category>bundlesize</category>
      <category>react</category>
      <category>performance</category>
    </item>
    <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;France&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;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;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;France&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;France&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Japan&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;France&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;Japan&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;France&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;France&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;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="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;Germany&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;Japan&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;France&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;France&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;France&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;France&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: [France], 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;France&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;France&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="nx"&amp;gt;Japan&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;France&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;Japan&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 799 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;EuropeanUnion&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;EuropeanUnion&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;Alpha-2 is the right value to store, but it is unguessable at the import site: &lt;code&gt;Bt&lt;/code&gt; is Bhutan, &lt;code&gt;Bj&lt;/code&gt; is Benin. So every country &lt;strong&gt;also&lt;/strong&gt; exports its full name as an alias, pointing at the same shape. Reach for whichever reads better where you write 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;UnitedStates&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;// same icon as `Us`&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;Bhutan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SouthKorea&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing changes about the storage advice: the alpha-2 code stays your canonical value in the database. The alias only spares you a lookup when you type the import.&lt;/p&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. The alias costs nothing here either: &lt;code&gt;UnitedStates&lt;/code&gt; and &lt;code&gt;Us&lt;/code&gt; resolve to one component, so the bundler ships it once. 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 I have to import GeoIcons by ISO code?
&lt;/h3&gt;

&lt;p&gt;No. Every country exports its full name as an alias next to the ISO one, so &lt;code&gt;UnitedStates&lt;/code&gt; and &lt;code&gt;Us&lt;/code&gt; are the same component, as are &lt;code&gt;Bhutan&lt;/code&gt; and &lt;code&gt;Bt&lt;/code&gt;. Import whichever reads better; both tree-shake to a single icon. The ISO code is still the value you should store in your database.&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;Japan&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;Japan&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;UnitedStates&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;UnitedStates&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;Italy&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;Italy&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;Japan&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;Japan&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;Italy&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;UnitedStates&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;Japan&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;UnitedStates&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;Italy&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;Japan&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;UnitedStates&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;UnitedStates&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;Italy&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;Italy&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>Why Map Shape Icons Beat Flag Emojis for Country Selectors</title>
      <dc:creator>Rushan</dc:creator>
      <pubDate>Fri, 17 Jul 2026 10:53:30 +0000</pubDate>
      <link>https://dev.to/rushan123/why-map-shape-icons-beat-flag-emojis-for-country-selectors-2oce</link>
      <guid>https://dev.to/rushan123/why-map-shape-icons-beat-flag-emojis-for-country-selectors-2oce</guid>
      <description>&lt;p&gt;Many developers use system emoji flags for country selectors. Pasting a flag emoji into code requires no library, adds zero bytes to a bundle, and inherits standard text styles. &lt;/p&gt;

&lt;p&gt;This approach carries design and technical limits. Emojis render in different ways across operating systems, and flags can clutter a modern user interface. Vector map shapes offer a clean, reliable alternative.&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;Windows does not support flag emojis, displaying two-letter country codes instead.&lt;/li&gt;
&lt;li&gt;Multi-colored flags clash with minimalist designs. Map shapes inherit your app's text color.&lt;/li&gt;
&lt;li&gt;Map shapes support custom trade zones and regional markets where flags do not exist.&lt;/li&gt;
&lt;li&gt;SVG shapes render the same on all devices and scale alongside your typography.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Windows rendering gap
&lt;/h2&gt;

&lt;p&gt;Microsoft Windows does not display flag emojis. Instead of the flag graphic, Windows displays the two-letter ISO country code inside a thin border. &lt;/p&gt;

&lt;p&gt;If your interface relies on flags to guide users, Windows visitors see a list of text codes. A user searching for Japan sees "JP". A user looking for the United Kingdom sees "GB". This behavior breaks visual layouts and slows down country selection.&lt;/p&gt;

&lt;p&gt;Using SVG map outlines solves this platform gap. SVGs use vector paths to draw the country boundaries. Every browser on Windows displays the shape as intended.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design consistency: Flags versus map shapes
&lt;/h2&gt;

&lt;p&gt;Flags contain complex designs, bright colors, and varied patterns. When you place multiple flags in a list, the visual noise competes for the user's attention. Emojis also fail to adapt to your application's theme. A bright red, white, and blue flag emoji stands out on a dark-themed dashboard, breaking your color palette.&lt;/p&gt;

&lt;p&gt;SVG map shapes provide design control. Because they draw outlines with &lt;code&gt;stroke="currentColor"&lt;/code&gt;, they inherit the CSS color of their container. When your application switches to dark mode, the map shapes transition with your text. &lt;/p&gt;

&lt;p&gt;With GeoIcons, every country shape fits a uniform grid, creating a balanced layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond ISO countries: Mapping markets and regions
&lt;/h2&gt;

&lt;p&gt;Flag emojis represent sovereign nations and specific territories. This mapping fails when your application needs to represent regional markets or continents. &lt;/p&gt;

&lt;p&gt;If your business serves the European Union, or groups shipping zones by West Africa or Asia-Pacific, a single flag emoji cannot represent the region. You end up mixing flags with text labels, creating an inconsistent list.&lt;/p&gt;

&lt;p&gt;SVG map shapes resolve this limit. You can represent geographical regions, continental groupings, or custom trade zones using the same visual style as country shapes. GeoIcons contains 167 area shapes alongside standard countries, letting you build cohesive region selectors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling and positioning
&lt;/h2&gt;

&lt;p&gt;Emojis are text characters, meaning their size and position depend on system font files. Aligning a flag emoji with text requires custom margins that vary by browser. Emojis also lose clarity when scaled, showing pixelation on high-resolution screens.&lt;/p&gt;

&lt;p&gt;SVG elements live in the DOM as vector graphics. You set their width and height in pixels, and they scale without losing sharpness. You can align them using flexbox or CSS grid like any other UI component.&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;UnitedStates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;England&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Japan&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;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;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-4 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;UnitedStates&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;"United States"&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;England&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;"England"&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;Japan&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;"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;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;h2&gt;
  
  
  Making the transition
&lt;/h2&gt;

&lt;p&gt;Replacing flags with map shapes improves both the design and the reliability of your interface. By switching to SVGs, you guarantee that every user sees the same interface, no matter their device.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Why do flag emojis not show up on Windows?
&lt;/h3&gt;

&lt;p&gt;Windows does not include flag designs in its system emoji font. The operating system renders the two-letter ISO code instead. To show a geographic graphic on Windows, you must use an image file or an SVG icon.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do SVG country outlines improve accessibility?
&lt;/h3&gt;

&lt;p&gt;Emojis rely on the system screen reader to announce their meaning, which varies by platform. SVG icons let you define custom aria-label attributes and title tags in the code, providing a predictable experience for screen reader users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does switching to SVG shapes increase my bundle size?
&lt;/h3&gt;

&lt;p&gt;This occurs if you load the entire library. When you use named exports from a library like GeoIcons, your bundler removes the icons you do not import, keeping your bundle small.&lt;/p&gt;

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

&lt;p&gt;Add GeoIcons to your project and replace your text emojis with consistent SVG elements:&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;UnitedStates&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;Selector&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;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;UnitedStates&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-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;&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;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;For styling options and import paths, consult the &lt;a href="https://geoicons.io/docs/api" rel="noopener noreferrer"&gt;API reference&lt;/a&gt;. To understand how GeoIcons maintains small bundles, read our article on &lt;a href="https://geoicons.io/blog/tree-shaking-icons" rel="noopener noreferrer"&gt;tree-shaking performance&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>design</category>
      <category>svgicons</category>
      <category>ux</category>
      <category>geoicons</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;Japan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;France&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Germany&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;EuropeanUnion&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;Japan&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;France&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;Germany&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;EuropeanUnion&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;a href="https://geoicons.io/blog/dark-mode-icons" rel="noopener noreferrer"&gt;See it live on geoicons.io →&lt;/a&gt;&lt;/p&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;France&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;France /&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;Germany&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;France&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;France&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;Japan&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;Japan&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 799 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: 799 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, 167 areas, and 377 subdivisions&lt;/strong&gt;, 799 icons in total at the time of this post, built for React, Vue, Angular, and vanilla JS. More is on the way, including 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;a href="https://geoicons.io/blog/introducing-geoicons" rel="noopener noreferrer"&gt;See it live on geoicons.io →&lt;/a&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;799 icons: 255 countries (ISO 3166) + 167 areas (regions, continents, landforms) + 377 subdivisions (states, provinces, parishes).&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;UnitedStates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;France&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Japan&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;UnitedStates&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;France&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;Japan&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;Nigeria&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;Nigeria&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;Nigeria&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;Nigeria&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;The same four, live:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://geoicons.io/blog/introducing-geoicons" rel="noopener noreferrer"&gt;See it live on geoicons.io →&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&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;UnitedStates&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;UnitedStates&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;UnitedStates&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;UnitedStates&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;UnitedStates&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;EuropeanUnion&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;UnitedStates&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;UnitedStates&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 799 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>
