--- title: Vector map docs-libs: [vector-map] description: Draw a world map as inline SVG from data attributes. Shade countries by value, add markers and lines, show tooltips and let users zoom, with no map library. summary: A vector map draws countries as SVG paths, shaded by a value, with markers, lines, tooltips and zoom. Tabler renders it from data attributes, with no map library and no map provider. added-in: '1.7.0' related: [/ui/components/map, /ui/components/sparkline] source: core/scss/ui/_vector-map.scss css-vars: vector-map classnames: component: - class: vector-map desc: Box that holds the map, as wide as its parent style: - class: vector-map-colors desc: Shades the values with a scale of several colors, set by JavaScript for data-bs-colors part: - class: vector-map-svg desc: The rendered SVG, added by JavaScript - class: vector-map-region desc: One country or region, with its code in data-region - class: vector-map-region-point desc: A country too small for a shape, drawn as a dot when it has a value - class: vector-map-region-point-dot desc: The dot, in the color of the region - class: vector-map-region-point-halo desc: Ring around the dot, in the surface color - class: vector-map-marker desc: Group that holds the two parts of a marker - class: vector-map-marker-dot desc: The colored dot of a marker - class: vector-map-marker-halo desc: Ring around the dot, in the surface color - class: vector-map-line desc: Dashed curve between two markers - class: vector-map-tooltip desc: Label that follows the pointer - class: vector-map-controls desc: Zoom buttons in the corner of the map - class: vector-map-legend desc: The scale under the map, two numbers around a bar - class: vector-map-legend-label desc: A number at one end of the scale - class: vector-map-legend-scale desc: The bar of the legend - class: vector-map-legend-step desc: One slice of the bar, colored like a region with the same share --- import Example from '@components/Example.astro'; import CodeDocs from '@components/CodeDocs.astro'; import { Code } from 'astro:components'; import { site } from '@shared/lib/site.ts'; ## Overview A vector map draws countries as SVG paths, so it stays sharp at any size. Use it to show a number per country, a few places, or routes between them. The component is Tabler's own. It needs no map library, no API key and no network request. The country shapes come from [Natural Earth](https://www.naturalearthdata.com/), which is public domain. ## Installation The vector map is not part of `tabler.js`, so pages without a map do not load it. Add two files after `tabler.js`: the component and the map data. `} /> The styles are part of `tabler.css`. With a bundler, import the component and the map, then register the map under a name: ```js import { VectorMap } from '@tabler/core/dist/js/tabler-vector-map.esm.js'; import { world } from '@tabler/core/dist/js/maps/world.esm.js'; VectorMap.addMap('world', world); ``` ## Default markup Add `class="vector-map"` and `data-bs-toggle="vector-map"` to an empty element. Tabler draws the map on page load. The map is as wide as its parent and keeps its own proportions, so it needs no height. Set `data-bs-label` to a short text that says what the map shows. Without it the map is hidden from screen readers.
## Values Pass a number per country in `data-bs-values`, as a JSON object. The keys are ISO 3166-1 alpha-2 codes, such as `PL` or `US`. The country with the highest value gets the full color and the others get a lighter shade.
The shades are spread between the lowest and the highest value. Set `data-bs-min` and `data-bs-max` to use a fixed range instead, for example `0` and `100` for a percentage. ### Color The map uses the primary color. Set `--tblr-vector-map-color` to change it.
### Color scale One color shows how much. A scale of several colors also shows good and bad, or cold and hot. Pass two to five colors in `data-bs-colors`, as a JSON array. The first color is for the lowest value and the last one for the highest. The values between them get a mix of the two nearest colors.
Any CSS color works, also a Tabler color such as `var(--tblr-red)`. The browser mixes the colors with `color-mix()`, so they follow the theme and you can change them later with CSS. The plugin writes the colors to `--tblr-vector-map-color-1`, `--tblr-vector-map-color-2` and so on, and their number to `--tblr-vector-map-colors`. To set a scale without JavaScript options, add the `vector-map-colors` class and these properties yourself: ```html
``` With a scale, the lowest value gets the first color in full, so `--tblr-vector-map-value-min` is not used. Markers keep the color from `--tblr-vector-map-color`. ### Legend Colors alone do not say how much. Add `data-bs-legend="true"` to show the scale under the map, as a bar between the lowest and the highest value. The bar uses the same colors as the map, also with a scale of several colors.
The two numbers are the range of the scale: `min` and `max` when you set them, the lowest and the highest value otherwise. Pass a function from JavaScript to add a unit or to format the numbers: ```js new tabler.VectorMap('#satisfaction', { values: { PL: 88, DE: 96 }, legend: (value) => `${value}%`, }); ``` Set the size of the bar with `--tblr-vector-map-legend-width` and `--tblr-vector-map-legend-height`. ## Markers Markers point at single places, such as offices or customers. Pass them in `data-bs-markers` as a JSON array. Each marker has a `lat`, a `lng` and an optional `name`. A marker keeps its size when the map is resized or zoomed.
## Lines Lines join two markers, for a route or a connection. Pass them in `data-bs-lines` as a JSON array. `from` and `to` are the names of two markers. The dashes move from the start of the line to its end, and stand still for users who ask for reduced motion.
## Zoom Add `data-bs-zoom="true"` to let users look closer. The map gets two buttons, and reacts to the mouse wheel, a double click, a pinch and a drag.
While the pointer is over a zoomable map, the wheel zooms the map and does not scroll the page. Set `data-bs-zoom-on-scroll="false"` to keep the wheel for the page. On a touch screen a finger on the map moves the map, not the page, so leave some room around a large map. ## Selecting countries Set `data-bs-select` to let users pick countries with a click. With `single` a click on another country moves the selection. With `multiple` every click adds a country. A click on a selected country takes it out again. Pass the countries that start selected in `data-bs-selected`.
A selected country gets an outline and keeps the color of its value. Change the outline with `--tblr-vector-map-selected-stroke` and `--tblr-vector-map-selected-stroke-width`. The map fires `selected.bs.vector-map` after every change. Read the selection from the event, or with `getSelected()`: ```js const element = document.getElementById('compare'); element.addEventListener('selected.bs.vector-map', (event) => { console.log(event.selected); // ['PL', 'FR', 'BR', 'DE'] }); tabler.VectorMap.getInstance(element).setSelected(['US', 'CA']); ``` ## Clicks The map fires `regionclick.bs.vector-map` when a country is clicked and `markerclick.bs.vector-map` when a marker is clicked, also when nothing can be selected. Use them to open a page or to filter a table. A drag on a zoomable map is not a click. ```js element.addEventListener('regionclick.bs.vector-map', (event) => { console.log(event.code, event.name, event.value); // 'PL', 'Poland', 54 }); element.addEventListener('markerclick.bs.vector-map', (event) => { console.log(event.name, event.lat, event.lng); }); ``` Call `event.preventDefault()` in `regionclick.bs.vector-map` to keep that click from changing the selection. ## Tooltip A tooltip shows the name of the country or marker under the pointer, with the value when the country has one. Set `data-bs-tooltip="false"` to turn it off. Pass a function from JavaScript to write your own text. It gets the item under the pointer and returns a string. The string is shown as plain text. Return an empty string to show nothing. ```js new tabler.VectorMap('#visits', { values: { PL: 54, DE: 80 }, tooltip: (item) => (item.type === 'region' && item.value !== undefined ? `${item.name}: ${item.value}%` : item.name), }); ``` ## Options Set options with data attributes or pass them to the constructor. For a data attribute, add `data-bs-` to the name and write it in kebab case, for example `data-bs-zoom-max`. | Name | Type | Default | Description | | --- | --- | --- | --- | | `map` | string, object | `'world'` | Name of a loaded map, or the map data itself. | | `values` | object | `{}` | A number per region code. | | `min` | number, null | `null` | Value that gets the lightest shade. The lowest value when `null`. | | `max` | number, null | `null` | Value that gets the full color. The highest value when `null`. | | `colors` | array | `[]` | Two to five colors of a scale, from the lowest value to the highest. | | `markers` | array | `[]` | Places as `{ name, lat, lng }`. | | `lines` | array | `[]` | Connections as `{ from, to }`, by marker name. | | `tooltip` | boolean, function | `true` | Shows a tooltip, or builds its text. | | `legend` | boolean, function | `false` | Shows the scale under the map, or formats its two numbers. | | `select` | string | `'none'` | Lets users select countries: `'none'`, `'single'` or `'multiple'`. | | `selected` | array | `[]` | Codes of the countries that start selected. | | `zoom` | boolean | `false` | Lets users zoom and drag the map. | | `zoomMax` | number | `8` | The largest magnification. | | `zoomOnScroll` | boolean | `true` | Zooms with the mouse wheel. Needs `zoom`. | | `zoomInLabel` | string | `'Zoom in'` | Accessible name of the zoom in button. | | `zoomOutLabel` | string | `'Zoom out'` | Accessible name of the zoom out button. | | `label` | string, null | `null` | Accessible name of the map. | ## JavaScript Tabler wraps the element in a `VectorMap` component that works like the Bootstrap components: it is created once per element and stored on it. When the page is loaded, Tabler creates one for every element with `data-bs-toggle="vector-map"`. This is the code that runs: Create the component yourself for an element added later, or to pass the data from code: ```js const visits = tabler.VectorMap.getOrCreateInstance(document.getElementById('visits'), { values: { PL: 54, DE: 80, FR: 66 }, }); ``` Call `update()` with new values to shade the map again. The map is not drawn again, so this is cheap enough for live data: ```js visits.update({ PL: 61, DE: 77, FR: 70 }); ``` | Method | Description | | --- | --- | | `update(values)` | Shades the regions with new values. Fires `updated.bs.vector-map`. | | `render()` | Reads the attributes again and draws the map from the start. Fires `rendered.bs.vector-map`. | | `getSelected()` | Returns the codes of the selected countries. | | `setSelected(codes)` | Selects exactly these countries. Fires `selected.bs.vector-map`. | | `zoomIn()` | Zooms in one step, around the middle of the view. | | `zoomOut()` | Zooms out one step. | | `reset()` | Goes back to the whole map. | | `dispose()` | Removes the map, the buttons, the legend, the tooltip and the component from the element. | | `VectorMap.addMap(name, data)` | Static. Registers a map under a name. | | `getInstance(element)` | Static. Returns the component for the element, or `null`. | | `getOrCreateInstance(element, config)` | Static. Returns the component for the element and creates it when needed. | | Event | Description | | --- | --- | | `rendered.bs.vector-map` | Fired on the element after every render. | | `updated.bs.vector-map` | Fired on the element after `update()`. | | `regionclick.bs.vector-map` | Fired when a country is clicked. Has `code`, `name`, `value` when the country has one, and the element in `relatedTarget`. Can be prevented. | | `markerclick.bs.vector-map` | Fired when a marker is clicked. Has `name`, `lat`, `lng` and the element in `relatedTarget`. | | `selected.bs.vector-map` | Fired after the selection changes. Has the codes in `selected`, and the clicked `code` when a click made the change. | ## Styling a single country Every country has its code in `data-region`, so one rule can color it. Set `--tblr-vector-map-region-color`, which works for a shape and for the dot of a small country: ```css .vector-map [data-region='PL'] { --tblr-vector-map-region-color: var(--tblr-red); } ``` ## Custom properties The map reads its colors and sizes from custom properties on the element. Change them for one map with a `style` attribute, or for a section with a class. | Property | Default | Description | | --- | --- | --- | | `--tblr-vector-map-color` | `var(--tblr-primary)` | Color of the highest value, and of the markers. | | `--tblr-vector-map-region-fill` | `var(--tblr-border-color)` | Fill of a country with no value. | | `--tblr-vector-map-region-stroke` | `var(--tblr-bg-surface)` | Border between countries. | | `--tblr-vector-map-region-stroke-width` | `1px` | Width of the border. | | `--tblr-vector-map-value-min` | `0.1` | Share of the color the lowest value gets, from 0 to 1. | | `--tblr-vector-map-color-1` to `-5` | none | Colors of a scale, from the lowest value to the highest. | | `--tblr-vector-map-colors` | none | Number of colors in the scale. | | `--tblr-vector-map-point-size` | `0.375rem` | Diameter of the dot of a small country. | | `--tblr-vector-map-selected-stroke` | `var(--tblr-body-color)` | Outline of a selected country. | | `--tblr-vector-map-selected-stroke-width` | `2px` | Width of that outline. | | `--tblr-vector-map-marker-size` | `0.5rem` | Diameter of a marker. | | `--tblr-vector-map-marker-color` | `var(--tblr-vector-map-color)` | Color of a marker. | | `--tblr-vector-map-marker-halo-color` | `var(--tblr-bg-surface)` | Color of the ring around a marker and around the dot of a small country. | | `--tblr-vector-map-marker-halo-width` | `2px` | Width of the ring. | | `--tblr-vector-map-line-color` | `var(--tblr-secondary)` | Color of a line. | | `--tblr-vector-map-line-width` | `1px` | Width of a line. | | `--tblr-vector-map-line-dash` | `4px` | Length of a dash and of the gap after it. | | `--tblr-vector-map-line-duration` | `1s` | Time one dash takes to move to the place of the next. | | `--tblr-vector-map-legend-width` | `8rem` | Width of the bar of the legend. | | `--tblr-vector-map-legend-height` | `0.5rem` | Height of the bar. | | `--tblr-vector-map-controls-offset` | `var(--tblr-spacer-2)` | Distance of the zoom buttons from the corner. | ## The world map The world map has 207 countries in the Miller projection, without Antarctica. Values for a code that is not on the map are skipped. ### Small countries 31 countries are too small to see as a shape on a map of the world, for example Andorra, San Marino, Malta or Singapore. The map shows such a country as a dot when it has a value. A dot works like any other country: it gets the same shade and has a tooltip. It keeps its size when the map is resized or zoomed.
The dots are the sovereign states that have no shape on the map, plus Hong Kong and Macao. Dependent territories, such as Gibraltar or Guam, are not on the map. Set the size of a dot with `--tblr-vector-map-point-size`. A small country with no value is hidden, so a map with a few markers does not fill up with grey dots. To show all of them, add this rule: ```css .vector-map-region-point:not([data-value]) { display: inline; } ``` Three territories have no ISO code. The map uses `XK` for Kosovo, `XC` for Northern Cyprus and `XS` for Somaliland. Borders follow the default view of Natural Earth. ## Accessibility - Set `data-bs-label`. The map is then an image with that name. The countries themselves are not read out one by one. - The data behind the map has to be reachable another way. Put a table of the same values next to or below the map. It serves screen reader users and anyone who prints the page. - The tooltip is for the pointer only. Do not put anything there that is not in the text version too. - Shades of one color are hard to tell apart for many users. Turn on the legend, and for exact numbers add a table. - Countries and markers can be clicked with a pointer only. They are not in the tab order, because a world map would be two hundred tab stops. When a click or a selection does something, offer the same choice in another control, such as a list of checkboxes or a select, and keep the two in step with `setSelected()`. - The zoom buttons work with the keyboard and have accessible names. Change the names with `data-bs-zoom-in-label` and `data-bs-zoom-out-label` when the page is not in English. ## Variables ## Migration notes Before 1.7 this page used [jsVectorMap](https://jvm-docs.vercel.app/). Its files are still in `dist/libs/jsvectormap` and its styles are still in `tabler-vendors.css`, so a page that uses it keeps working. The demo and the docs now use the component above.