Make your icons follow dark mode automatically with currentColor
- dark-mode
- currentcolor
- svg-icons
- theming
- geoicons
Most icon sets bake a color into the SVG. The fill is set to #111 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.
GeoIcons take the other approach. Each icon draws its outline with stroke="currentColor", so it reads the CSS color of whatever wraps it. Set the text color for your light and dark themes once, and every icon on the page follows along:
import { Jp, Fr, De } from '@geoicons/react/countries';
import { EuropeanUnionEu } from '@geoicons/react/areas';
// None of these set a color. They all inherit the text color of the wrapper.
<div className="text-slate-900 dark:text-slate-100">
<Jp size={40} />
<Fr size={40} />
<De size={40} />
<EuropeanUnionEu size={40} />
</div>Toggle this site's dark mode by clicking the icon in the top-right corner of the header to see it in action.
Key takeaways
- Every GeoIcon draws with
stroke="currentColor", so it inherits the CSScolorof its parent. - To support dark mode, set
colorfor each theme in CSS. You never touch the icon. - A Tailwind class like
dark:text-slate-100works directly on the icon or on any ancestor.
What does currentColor do?
currentColor is a CSS keyword that means "the computed value of this element's color property." A GeoIcon sets stroke="currentColor" on its <svg>, so the outline paints in whatever color the icon inherits. Change the color anywhere up the tree, and the stroke changes with it.
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.
<p style={{ color: '#e11d48' }}>
<Fr aria-hidden="true" /> France
</p>The <Fr /> outline turns rose because it inherits color: #e11d48 from the paragraph.
Why do hardcoded icon colors break in dark mode?
An icon with a fixed fill="#111827" 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.
currentColor removes that work. The icon holds no color, so there is nothing to override. You style the container, and the icon inherits the result.
How do I switch icons for a dark theme?
Pick the color for each theme and let the icon inherit it. If you toggle dark mode with a class on <html> (the common pattern in Tailwind and most CSS setups), scope the color to that class:
:root {
color: #0f172a; /* slate-900 for light mode */
}
.dark {
color: #e2e8f0; /* slate-200 for dark mode */
}Every icon under <html class="dark"> now paints in #e2e8f0. With Tailwind you can skip the CSS and put the two colors on the icon itself:
<De
className="text-slate-900 dark:text-slate-100"
aria-label="Germany"
/>The text-* utilities set color, and the icon's currentColor stroke picks it up. One element, both themes.
How do I follow the system setting instead?
Read the operating system preference with the prefers-color-scheme media query and set color from it. This needs no JavaScript and no class toggle:
:root {
color: #0f172a;
}
@media (prefers-color-scheme: dark) {
:root {
color: #e2e8f0;
}
}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.
Can I recolor an icon on hover?
Yes. Because the icon follows color, any state that changes color also changes the icon. A hover on the button recolors the outline inside it:
.icon-button {
color: #64748b; /* slate-500 at rest */
}
.icon-button:hover {
color: #2563eb; /* blue-600 on hover */
}<button className="icon-button" aria-label="Filter by France">
<Fr aria-hidden="true" />
</button>The same idea covers focus rings and disabled states. You style the button, and the icon comes along.
Outline or solid: stroke versus fill
GeoIcons render as outlines. The stroke is currentColor and the fill is none, which is why theming runs through the stroke. If you want a solid glyph, set fill yourself and keep it on currentColor so it still follows the theme:
<Fr fill="currentColor" stroke="none" aria-label="France" />Both the stroke path and a currentColor fill respect the same inherited color, so a solid icon themes exactly like an outline one.
FAQ
- How do I make an icon change color in dark mode?
- 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.
- Do I need a color prop on every GeoIcon?
- 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.
- Can I give one icon a different color from the surrounding text?
- 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.
Get started
Install the package and let your theme drive the color:
npm i @geoicons/reactimport { Jp } from '@geoicons/react/countries';
export default function App() {
return (
<span className="text-slate-900 dark:text-slate-100">
<Jp aria-label="Japan" />
</span>
);
}For every prop each icon accepts, see the GeoIcons API reference. To learn why importing icons this way keeps your bundle small, read why an icon library doesn't have to bloat your bundle. New here? Start with adding your first country icon or browse all 422 icons.