Skip to main content
Skip to main content

Customization

Custom Styling​

This is an optional more advanced customization option. When used, the developer has full control to define the color, opacity and any other style element of a country with data record.

This is done by passing your custom implementation of the styleFunction. The function receives as input the country context that includes country, countryValue, color, minValue and maxValue, and returns a React CSSProperties object. Note that countryValue can be a number, string, or undefined, for which case you may need to handle the styling differently.

For example:

import { CountryContext } from "react-svg-worldmap";

const stylingFunction = ({
countryValue,
minValue,
maxValue,
country,
color,
}: CountryContext) => {
const calculatedValue =
typeof countryValue === "string" ? minValue : countryValue;
const opacityLevel =
calculatedValue !== undefined
? 0.1 + (1.5 * (calculatedValue - minValue)) / (maxValue - minValue)
: 0;
return {
fill: country === "US" ? "blue" : color,
fillOpacity: opacityLevel,
stroke: "green",
strokeWidth: 1,
strokeOpacity: 0.2,
cursor: "pointer",
};
};

Styling and testing hooks​

The map's SVG carries stable attributes you can target from CSS or tests, so you don't depend on element order:

  • Each country <path> has data-country-code (ISO code, e.g. US) and data-country-name (e.g. United States).
  • The map is drawn in layers. Each layer is a <g> directly under the <svg> with data-map-layer and data-map-layer-order (paint order, back to front): ocean, countries, disputed-territories (only with showDisputedTerritories), labels (from textLabelFunction), country-labels (only with zoom), and pins (only with pins). The list is exported as mapRenderingLayers. Layers that move with the map share the countries layer's transform.
  • Each pin is a <g data-map-pin> (its kind, default pin) with data-country-code when the pin has a countryCode. Each country label is a <text data-country-code> in the country-labels layer.
  • With zoom, the <svg> has data-zoom-scale (the current zoom level) and sits in a <div class="worldmap__zoom-viewport"> together with the zoom buttons.
  • The ocean layer holds one <rect data-map-ocean> filled with backgroundColor. It has the same colour as the figure behind it, so the map looks the same as before.
  • With frame, the frame is a <rect data-map-frame> drawn between the ocean and the countries.
.my-map [data-map-ocean] {
fill: #a0d7eb;
}
.my-map [data-country-code="FR"] {
stroke-width: 2;
}
screen.getByRole("img").querySelector('[data-country-code="US"]');

Sizing​

The size can be a number specifying the width of the map, in pixels. It can also be one of the following aliases:

AliasWidth
sm240
md336
lg480
xl640
xxl1200

When the size is set to responsive, the map fills the width of its container and grows and shrinks continuously as the container resizes. The height is always width * 3/4.

Because the map is not capped by the viewport height, a wide container can produce a map taller than the screen. To limit it, constrain the container, for example with max-width, or pass a number as the size.

The container needs a width that doesn't depend on its content, such as a block element or a flex item with width: 100% or flex: 1. In a shrink-to-fit container (a flex item with no width or flex-grow, inline-block, a float, or an auto grid column), the map and its container size each other, so the map can shrink but will not grow back.

info

The algorithm used in responsive sizing is:

const width = availableWidth; // measured container width

Rich interactions​

richInteraction captures a few events inside the SVG:

  • Double-clicking zooms in twice, then restores the original scale.
  • With the map focused, + zooms in to the centre (up to 4x) and - resets.

Zoom, pins and labels​

For full zoom and pan, pass zoom instead: buttons, drag, double-click, keyboard, and ctrl / ⌘ + wheel or trackpad pinch, with maxScale and other options. It also shows country names that fit inside their countries. pins draws captioned markers at longitude / latitude, with or without zoom.

<WorldMap
data={data}
zoom={{ maxScale: 6 }}
pins={[{ caption: "Paris", coordinates: [2.35, 48.86], countryCode: "FR" }]}
/>

Their colors are props, so they can follow your theme (for example a dark mode):

PropDefaultUsed for
pinColor#b91c1cFill of the pin markers.
pinTextColor#1f2937Pin captions.
pinOutlineColor#fffOutline of the markers and halo behind the captions.
labelColor#222Country labels.
labelHaloColornoneOptional halo behind the country labels.

The zoom buttons can be styled through [aria-label="Map zoom controls"] button, or hidden with zoom={{ showControls: false }}.

Projections​

WorldMap uses Mercator by default. Pass projection="equalEarth" for the Equal Earth equal-area projection, where countries keep their true relative size.

<WorldMap data={data} projection="equalEarth" />
  • Default stays mercator, so existing maps are unchanged.
  • Equal Earth uses a 2:1 SVG frame (height = width / 2) and draws a globe outline in borderColor.
  • Tooltips, callbacks, richInteraction, zoom, pins, and showDisputedTerritories work in both projections.
  • textLabelFunction positions are SVG pixels; labels placed for Mercator may need new coordinates in Equal Earth.

See the Equal Earth example.