Styling
Every React Simple Maps component renders a plain SVG element, adds one rsm- class to it, and spreads any prop it does not recognise straight onto that element. That gives you four ways to style a map, in rough order of how much you need:
- Attribute styling — props for static, per-element looks
- CSS styling — classes for shared looks and interaction states
- CSS variable styling — per-feature colours with CSS-driven hover, the best fit for choropleths
- CSS variables with Tailwind — The CSS variable approach can also be easily combined with Tailwind
The classes are fixed:
<svg class="rsm-svg">
<g class="rsm-zoomable-group">
<path class="rsm-graticule" />
<path class="rsm-sphere" />
<g class="rsm-geographies">
<path class="rsm-geography" />
<path class="rsm-geography" />
<path class="rsm-geography" />
...
</g>
<g class="rsm-marker"></g>
<g class="rsm-marker"></g>
<g class="rsm-marker"></g>
...
<g class="rsm-annotation"></g>
<g class="rsm-annotation"></g>
<g class="rsm-annotation"></g>
...
<path class="rsm-line" />
<path class="rsm-line" />
<path class="rsm-line" />
...
</g>
</svg>Your className is added to the built-in one rather than replacing it, so <Geography className="state" /> renders class="rsm-geography state".
Attribute styling
SVG presentation attributes passed as props are passed on to the element:
<Geography
geography={geo}
fill="#EAEAEC"
stroke="#FFF"
strokeWidth={0.5}
strokeLinejoin="round"
/>Some components have default props you may want to override — Line defaults to stroke="currentcolor" and strokeWidth={3}, Graticule and Sphere have a transparent fill and currentcolor stroke.
<div style={{ color: "#B0B7C3" }}>
<ComposableMap>
<Graticule /> {/* strokes in #B0B7C3 */}
<Sphere />
</ComposableMap>
</div>Attributes are the right tool for values that come from data and never change on interaction. They cannot express :hover or :focus — for that, use CSS.
The style prop
style also passes straight through, and takes precedence over presentation attributes:
<Geography geography={geo} style={{ fill: "#0066ff", cursor: "pointer" }} />Note this is a plain React style object. Some older React Simple Maps examples show a style={{ default: …, hover: …, pressed: … }} shape — that was a v3 API and does not work in v5. The equivalent now is CSS classes or CSS variables.
CSS styling
For anything shared across many features, or any interaction state, style the rsm- classes in a stylesheet. This keeps per-feature React props to a minimum, which matters on maps with hundreds of paths.
.rsm-geography {
fill: #eaeaec;
outline: none;
transition: fill 100ms ease;
}
.rsm-geography:hover {
fill: #ff5533;
}
.rsm-geography:active {
fill: #e03e20;
}<Geographies geography={geoUrl}>
{({ geographies }) =>
geographies.map((geo) => <Geography key={geo.rsmKey} geography={geo} />)
}
</Geographies>Note the CSS uses hyphenated stroke-width, where the React prop is strokeWidth.
Focus states
Geography renders with tabIndex={0}, so every feature is keyboard focusable and will show the browser's default focus ring. Rather than removing it, replace it with something visible:
.rsm-geography {
outline: none;
}
.rsm-geography:focus-visible {
stroke: #000000;
stroke-width: 2;
}If a map is decorative and should not be in the tab order at all, pass tabIndex={-1}.
Scoping to one map
The rsm- classes are global. With more than one map on a page, scope your rules through a wrapper or the map's own className:
<ComposableMap className="world-map">…</ComposableMap>.world-map .rsm-geography {
fill: #dfe3e8;
}CSS variable styling
Attributes give you per-geography color but no hover. CSS gives you hover but the same color everywhere. Choropleths may need both: each geography colored by its own data value, and a hover state that fits that color.
CSS custom properties bridge the two. Set the per-geography values in JS via style, and consume them in a stylesheet that owns the interaction states:
import { scaleQuantize } from "d3-scale"
const colorScale = scaleQuantize()
.domain([0, 100])
.range(["#d4e8f7", "#a8d0ee", "#7cb8e5", "#5090c9", "#2468ad"])
const hoverColor => () => ...
<Geographies geography={geoUrl}>
{({ geographies }) =>
geographies.map((geo) => {
const value = data[geo.id]
const fill = value == null ? "#F0F0F0" : colorScale(value)
return (
<Geography
key={geo.rsmKey}
geography={geo}
style={{
"--fill": fill,
"--fill-hover": hoverColor(fill),
}}
/>
)
})
}
</Geographies>.rsm-geography {
fill: var(--fill, #f0f0f0);
stroke: #fff;
stroke-width: 0.5;
outline: none;
transition: fill 150ms ease;
}
.rsm-geography:hover {
fill: var(--fill-hover, #ff5533);
}
.rsm-geography:focus-visible {
stroke: var(--fill-hover, #0066ff);
stroke-width: 2;
}The variables carry the data; the stylesheet owns every state. Adding a pressed or selected state later is a CSS change, not a React one. Always give var() a fallback so geographies with no data still render.
Bonus: Deriving the hover color
The point of --fill-hover is that it fits the generated color rather than being one shared accent. The package already depends on d3-color, so you can derive it:
import { color } from "d3-color"
const hoverColor = (fill) => color(fill).brighter(0.4).formatHex()brighter() alone breaks down at the extremes — it cannot brighten pure white, and on a dark palette a fixed step can blow out. Choosing the direction based on lightness is more robust:
import { hcl } from "d3-color"
function hoverColor(fill) {
const c = hcl(fill)
c.l += c.l > 60 ? -12 : 14 // darken light fills, lighten dark ones
return c.formatHex()
}Working in HCL keeps hue and chroma fixed and moves only perceived lightness, so the hover state reads as the same colour rather than a different one.
Setting variables higher up
Variables inherit, so shared values belong on an ancestor rather than repeated on every path:
<ComposableMap style={{ "--stroke": "#FFF", "--fill-empty": "#F0F0F0" }}>.rsm-geography {
fill: var(--fill, var(--fill-empty));
stroke: var(--stroke);
}Only the values exclusive to a geography need to be set on the Geography itself.
TypeScript
React.CSSProperties does not include custom properties, so TypeScript rejects "--fill" in a style object. Cast them explicitly:
style={{ "--fill": fill } as React.CSSProperties}Or, to keep it checked, declare the properties you use:
interface MapVars extends React.CSSProperties {
"--fill"?: string
"--fill-hover"?: string
}
const style: MapVars = { "--fill": fill, "--fill-hover": hoverColor(fill) }CSS variables with Tailwind
You can easily access CSS variables in utility classes from Tailwind. If you are already using Tailwind for your project, this is usually the easiest approach.
<Geography
geography={...}
style={{ "--fill": "#000000", "--fill-hover": "#333333" }}
className="fill-(--fill) hover:fill-(--fill-hover)"
/>