Frontend
How to add country icons to a Vue 3 app
Rushan DEV Community
2 views
Vue has no shortage of icon sets. Carets and chevrons come in every pack. Geography is the thin part of the shelf: where a set covers countries at all, it usually means flag squares rather than map shapes. GeoIcons ships country, area, and subdivision icons as individual Vue 3 components, 799 of them at the time of writing. Adding one takes three steps: install the package, import the icon by its country name, and render it.
npm i @geoicons/vue
<script setup lang="ts">
import { UnitedStates, France, Japan } from '@geoicons/vue/countries';
</script>
<template>
<UnitedStates :size="40" aria-label="United States" />
<France :size="40" aria-label="France" />
<Japan :size="40" aria-label="Japan" />
</template>
See it live on geoicons.io →
That is the whole path. Below: sizing, color, accessibility, and picking an icon when you only know the country at runtime.
Key takeaways
Install @geoicons/vue, then import each country by its full name in PascalCase: UnitedStates, France, Japan.
Icons are Vue 3 components. Bind :size and :stroke-width; everything else falls through to the <svg>.
Icons are decorative by default. Pass aria-label only when the icon is the sole thing naming the country.
For a country chosen at runtime, pair a small component map with <component :is> so the bundle stays tree-shakable.
Step 1: Install the Vue package
Add the package to your project:
npm i @geoicons/vue
It needs Vue 3.0 or newer and pulls in nothing else at runtime. On Vue 3.5+ each icon takes its ids from the built-in useId(), which stays stable across server and client renders. On 3.0 to 3.4 it falls back to a per-instance id, so older projects work without a flag.
The package ships as ES modules and sets "sideEffects": false. Vite reads your static imports at build time and keeps only the icons you named, so a three-icon app pays for three icons. How that works in detail.
Step 2: Import by country name
Each country is a named export, PascalCased: UnitedStates, France, Japan. Know the country and you know what to type, so autocomplete finishes the rest:
import { UnitedStates, France, Japan, Germany, Brazil } from '@geoicons/vue/countries';
That line pulls the United States, France, Japan, Germany and Brazil. Reach for the /countries subpath rather than the package root: it opens the countries entry point directly, instead of a barrel that touches every category on the way through.
Not sure which name to import? Browse the full catalog. Each icon still carries its ISO 3166 alpha-2 code as an alias, Us alongside UnitedStates, if you need the shorter form.
Step 3: Render and style
Each import is an ordinary Vue component. Two props are declared, size and strokeWidth, and both accept a number or a string:
<template>
<Germany :size="48" />
<Germany :size="48" stroke="#2563eb" />
<Germany :size="48" :stroke-width="1.5" />
<Germany :size="48" fill="currentColor" />
</template>
Prop
What it does
size
Width and height. Number or any CSS length. Defaults to 24.
strokeWidth
Outline thickness. Bind as :stroke-width in templates. Defaults to 1.
stroke
Outline color. Defaults to currentColor.
fill
Fill color. Set to currentColor for a solid shape.
aria-label
Accessible name. Omit for a decorative icon.
The same four, live:
See it live on geoicons.io →
In a template you bind the kebab-case :stroke-width. In a render function or JSX you write strokeWidth. Vue accepts both spellings, and the icons declare the camelCase one.
Everything else falls through
Only size and strokeWidth are declared props. stroke, fill, class, style, @click, data-* and any other attribute lands directly on the underlying <svg> through attribute fallthrough:
<UnitedStates class="text-blue-600 hover:text-blue-800" @click="select('us')" />
Fallthrough is also why there is no color prop. Stroke defaults to currentColor, so each icon takes the color of the text around it. A theme switch that changes text color changes the icons with it, and no icon needs its own binding. The full dark-mode pattern.
Make it accessible
Icons are decorative by default. Each one renders with aria-hidden="true" unless you give it a name, so you do not need to add aria-hidden yourself:
<li><UnitedStates /> United States</li>
The text already says "United States", so a screen reader should skip the icon. It does.
When the icon is the only thing conveying the country, pass aria-label. The component then switches to role="img", renders a <title>, and wires up aria-labelledby for you:
<button @click="select('us')">
<UnitedStates aria-label="United States" />
</button>
Because each instance derives its own id, rendering the same country twice on one page never produces duplicate <title> ids.
How do I render a country icon at runtime?
A country picker knows the country as a string, not as a component. GeoIcons has no <Icon name="us" /> lookup on purpose: a string API would force the package to retain every icon in the catalog in case you asked for one. Build the map yourself and hand it to Vue's dynamic component:
<script setup lang="ts">
import type { Component } from 'vue';
import { UnitedStates, France, Japan } from '@geoicons/vue/countries';
type Code = 'us' | 'fr' | 'jp';
// Three entries, because you imported three icons.
const byCode: Record<Code, Component> = { us: UnitedStates, fr: France, jp: Japan };
defineProps<{ code: Code }>();
</script>
<template>
<component :is="byCode[code]" :size="20" />
</template>
Declare Code as its own alias rather than deriving it with keyof typeof byCode. The defineProps macro resolves its type argument at compile time, and a plain local alias is the form it handles everywhere.
Vite still reads three static imports at the top of the file and prunes everything else. You get the runtime lookup without the catalog behind it.
Beyond countries: areas and subdivisions
The same import pattern covers the area icons, 167 of them today: continents, regions, landforms, and groupings like the European Union. There is no ISO code to reach for here, so the export name is a PascalCase slug instead:
<script setup lang="ts">
import { EuropeanUnion } from '@geoicons/vue/areas';
</script>
<template>
<EuropeanUnion aria-label="European Union" />
</template>
GeoIcons also ships subdivisions: states, provinces, and other first-level regions, 377 of them, imported from their own subpath. ISO 3166-2 codes are numeric for eleven Caribbean countries, so GeoIcons builds the export name from the parent country's alpha-2 code plus the region name instead, both PascalCase: UsTexas, JmKingston.
<script setup lang="ts">
import { UsTexas } from '@geoicons/vue/subdivisions';
</script>
<template>
<UsTexas aria-label="Texas" />
</template>
FAQ
How do I add a country icon to a Vue app?
Install @geoicons/vue, import the country by its full name in PascalCase, and render it as a component. For example, import { UnitedStates } from '@geoicons/vue/countries' gives you a component that takes a size and inherits its color from CSS.
How do I show an icon for a country chosen at runtime in Vue?
Import the countries you need, build a small object mapping codes to components such as { us: UnitedStates, fr: France }, and render it with Vue's dynamic component: <component :is=\
Does GeoIcons cover states and provinces, not just countries?
Yes, 377 subdivision icons alongside 255 countries and 167 areas. Import them from @geoicons/vue/subdivisions using the parent country's alpha-2 code plus the region name in PascalCase, such as UsTexas or JmKingston, since ISO 3166-2 codes are numeric for several Caribbean countries.
Do GeoIcons work with Nuxt and server-side rendering?
Yes. The icons render to plain SVG markup and touch no browser-only APIs, so the server can render them directly. On Vue 3.5 and newer the title ids come from Vue's built-in useId(), which returns the same id on the server and in the browser, so hydration does not mismatch.
Where to go next
The full prop and import reference lives in the GeoIcons API reference, or browse the full catalog to find the ones you need. Working in another framework? The same catalog ships for React, Angular, and vanilla JavaScript.
Read original: https://dev.to/rushan123/how-to-add-country-icons-to-a-vue-3-app-13db
← Previous
I Asked My AI Assistant to Automate Our Dev Marketing. Here Are the Mistakes It Made.
Next →
Can Qwen 3.8 running on your laptop really replace Claude Opus for Agentic coding?
Related
Indexing Like a Pro: Lessons from The Matrix
Frontend
1
Dev.to (EN Zone)
Two Sources of Truth Will Always Disagree
Frontend
2
DEV Community
Realtime Audit Signals for Voice Lobby Fan-Out (and Why Delivery Evidence Matters)
Frontend
1
DEV Community
Odoo CRM Implementation: 7 Practical Lessons From Real ERP Projects
Frontend
1
Dev.to (EN Zone)
Comments0
No comments yet — be the first