const cloudflareLocations = [
  { city: "San Francisco", iata: "SFO", lat: 37.77, lon: -122.42 },
  { city: "London", iata: "LHR", lat: 51.51, lon: -0.13 },
  { city: "Singapore", iata: "SIN", lat: 1.35, lon: 103.82 },
  // ...more locations
];

<BubbleMap
  echarts={echarts}
  geoJson={geoJson}
  data={cloudflareLocations}
  lng="lon"
  lat="lat"
  name="city"
  value={() => 1}
  bubbleColor="#F6821F"
  minRadius={8}
  maxRadius={8}
  tooltipFormatter={(row) =>
    "<strong>" + row.city + "</strong> " + row.iata
  }
/>

Installation

BubbleMap and ChoroplethMap require echarts as a peer dependency. Consumers provide the GeoJSON feature collection; map components do not fetch map data or use map tiles.

npm install echarts

Barrel

import { BubbleMap, ChoroplethMap } from "@cloudflare/kumo";

Granular

import { BubbleMap, ChoroplethMap } from "@cloudflare/kumo/components/chart";

Usage

import { BubbleMap, ChoroplethMap, type MapGeoJson } from "@cloudflare/kumo";
import * as echarts from "echarts/core";
import { MapChart, ScatterChart } from "echarts/charts";
import { TooltipComponent, VisualMapComponent } from "echarts/components";
import { CanvasRenderer } from "echarts/renderers";

echarts.use([
  MapChart,
  ScatterChart,
  TooltipComponent,
  VisualMapComponent,
  CanvasRenderer,
]);

// Load GeoJSON in your app and pass it to map components.
const geoJson = world as MapGeoJson;

const colos = [
  { iata: "SFO", city: "San Francisco", lat: 37.77, lon: -122.42, requests: 1200 },
  { iata: "LHR", city: "London", lat: 51.5, lon: -0.12, requests: 1500 },
];

const countries = [
  { country: "United States of America", requests: 4200 },
  { country: "Germany", requests: 3100 },
];

export default function Example() {
  return (
    <>
      <BubbleMap
        echarts={echarts}
        geoJson={geoJson}
        data={colos}
        lng="lon"
        lat="lat"
        name="city"
        value="requests"
      />

      <ChoroplethMap
        echarts={echarts}
        geoJson={geoJson}
        data={countries}
        name="country"
        value="requests"
      />
    </>
  );
}

Examples

Bubble Map

Plot raw rows by longitude and latitude. The value accessor controls proportional bubble size.

<BubbleMap
  echarts={echarts}
  geoJson={geoJson}
  data={colos}
  lng="lon"
  lat="lat"
  name="city"
  value="requests"
  minRadius={8}
/>

Choropleth Map

Shade regions by value. Data rows are joined to GeoJSON features by name (matched against the feature's nameProperty, default "name").

<ChoroplethMap
  echarts={echarts}
  geoJson={geoJson}
  data={countries}
  name="country"
  value="requests"
/>

Custom Tooltips

Provide tooltipFormatter when the default name/value tooltip is not enough. The formatter returns HTML rendered by ECharts, so escape user-provided values.

<BubbleMap
  echarts={echarts}
  geoJson={geoJson}
  data={colos}
  lng="lon"
  lat="lat"
  name="city"
  value="requests"
  tooltipFormatter={(row) =>
    "<strong>" + row.city + "</strong><br />" + row.requests.toLocaleString()
  }
/>

API Reference

BubbleMap

PropTypeDefaultDescription
echarts*typeof echarts-The ECharts core instance imported by the consumer (passed in for tree-shaking). Requires `MapChart`, `ScatterChart`, `TooltipComponent`, and a renderer registered via `echarts.use([...])`.
geoJson*MapGeoJson-GeoJSON `FeatureCollection` for the land base.
mapNamestring-Optional stable ECharts map registry name. Set this when the same GeoJSON is parsed into new object instances across mounts and should reuse one global ECharts registration.
data*T[]-Raw data rows. Coordinates/value/name are read via the accessors below.
lng*MapAccessor<T, number>-Longitude accessor (key of `T` or `(row) => number`).
lat*MapAccessor<T, number>-Latitude accessor (key of `T` or `(row) => number`).
value*MapAccessor<T, number>-Value accessor — drives bubble size.
nameMapAccessor<T, string>-Optional name accessor — used by the default tooltip.
minRadiusnumber-Smallest bubble radius in px. Default: `6`.
maxRadiusnumber-Largest bubble radius in px. Default: `26`.
bubbleSize(value: number) => number-Explicit bubble radius `(value) => px`. Overrides the default `minRadius`/`maxRadius` scaling.
bubbleColorMapStyle<T, string>-Bubble fill colour — constant or `(row) => color`. Defaults to the chart blue.
bubbleBorderColorMapStyle<T, string>-Bubble border colour — constant or `(row) => color`. Default: `transparent`.
bubbleBorderWidthMapStyle<T, number>-Bubble border width — constant or `(row) => px`. Default: `0`.
center[number, number]-Map center as `[longitude, latitude]`. Defaults to auto-fit.
zoomnumber-Zoom level — multiplies the auto-fit scale. Default: `1.25`.
roamboolean-Enable drag-to-pan and scroll-to-zoom. Default: `false`.
projectionMapProjection | null-Geographic projection. Defaults to a latitude-clamped Mercator (flat 2D web-map look). Pass another d3-geo projection to override, or `null` for ECharts' raw equirectangular plotting. Use a stable reference (module-level or memoised): a new object each render rebuilds the view and resets a roamed/zoomed map.
showTooltipboolean-Show the tooltip. Default: `true`.
valueFormat(value: number) => string-Format the value for the default tooltip. Default: `toLocaleString()`.
tooltipFormatter(row: T) => string-Override the tooltip content for a row. Returns an HTML string rendered by ECharts' own tooltip. USE WITH CAUTION: the return value is injected as HTML. Escape any user-provided strings to avoid XSS.
aspectRationumber | string-Container aspect ratio as `width / height` (e.g. `1.7` or `"16 / 9"`). The height derives from the rendered width so the map fills the frame with no letterboxing. Defaults to the projected aspect of the displayed window, so the land fits edge-to-edge. Pass `height` to fix a pixel height instead.
heightnumber-Fixed chart height in pixels. Overrides `aspectRatio` when set. Leave unset to size by aspect ratio (the default) so the map fills the container.
classNamestring--
isDarkModeboolean--

ChoroplethMap

PropTypeDefaultDescription
echarts*typeof echarts-The ECharts core instance imported by the consumer (passed in for tree-shaking). Requires `MapChart`, `VisualMapComponent`, `TooltipComponent`, and a renderer registered via `echarts.use([...])`.
geoJson*MapGeoJson-GeoJSON `FeatureCollection` whose regions are shaded by value.
mapNamestring-Optional stable ECharts map registry name. Set this when the same GeoJSON is parsed into new object instances across mounts and should reuse one global ECharts registration.
data*T[]-Raw data rows. The region key and value are read via the accessors below.
name*MapAccessor<T, string>-Region-key accessor (key of `T` or `(row) => string`). Each row is joined to a GeoJSON feature whose `nameProperty` equals this value.
value*MapAccessor<T, number>-Value accessor — drives the region's fill colour.
namePropertystring-GeoJSON feature property to join on. Default: `"name"`. Real-world data is often more reliably matched on an ISO-code property (e.g. `"iso_a2"`).
colorRangestring[]-Sequential colour ramp (low → high). Defaults to the Kumo choropleth blues, tuned to stay distinct from the no-data fill. Distributed across the continuous gradient.
noDataColorstring-Fill for regions with no matching data row. Defaults to the neutral land grey.
showLegendboolean-Show the visualMap colour legend. Default: `false`.
showTooltipboolean-Show the tooltip. Default: `true`.
valueFormat(value: number) => string-Format the value for the default tooltip. Default: `toLocaleString()`.
tooltipFormatter(row: T) => string-Override the tooltip content for a row. Returns an HTML string rendered by ECharts' own tooltip. USE WITH CAUTION: the return value is injected as HTML. Escape any user-provided strings to avoid XSS.
center[number, number]-Map center as `[longitude, latitude]`. Defaults to auto-fit.
zoomnumber-Zoom level — multiplies the auto-fit scale. Default: `1.25`.
roamboolean-Enable drag-to-pan and scroll-to-zoom. Default: `false`.
projectionMapProjection | null-Geographic projection. Defaults to a latitude-clamped Mercator (flat 2D web-map look). Pass another d3-geo projection to override, or `null` for ECharts' raw equirectangular plotting. Use a stable reference (module-level or memoised): a new object each render rebuilds the view and resets a roamed/zoomed map.
aspectRationumber | string-Container aspect ratio as `width / height` (e.g. `1.7` or `"16 / 9"`). The height derives from the rendered width so the map fills the frame with no letterboxing. Defaults to the projected aspect of the displayed window, so the regions fit edge-to-edge. Pass `height` to fix a pixel height instead.
heightnumber-Fixed chart height in pixels. Overrides `aspectRatio` when set. Leave unset to size by aspect ratio (the default) so the map fills the container.
classNamestring--
isDarkModeboolean--