React components for data driven choropleth maps of Chile. The package includes processed BCN cartography for regions and lazy loadable provincia/comuna maps scoped to each region.
- Three ready to use components:
ChileMap(16 regions),RegionComunasMap, andRegionProvinciasMap(one region subdivided into comunas/provincias), all sharing the same props. - Data driven choropleths: provide
{ officialCode: value }and the component resolves colors, interactions, and tooltips. Values join by official codes (regions"01"-"16", comuna CUT, provincia codes). - Bundled cartography, lazy by region: the national TopoJSON is ~151 KB; each region's comunas/provincias ship as separate 2-102 KB files imported on demand and cached.
- Full territory with automatic insets: Rapa Nui and Juan Fernández render in side inset boxes with their own projection;
oceanicIslands={false}switches to a mainland only view. - Three scale types plus
<Legend>:linear,quantile, andthresholdscales built from a shared descriptor (scaleFromData), so the legend can never diverge from the map.colorForgives per unit control. - Interactions built in: hover tooltips (default, custom render prop, or disabled, with edge collision handling), click callbacks with semantic feature types, and controlled selection with
stroke/fill/bothhighlight modes. - Keyboard accessible: with
onClick, units becomerole="button"paths with a roving tab index, arrow-key navigation, Enter/Space activation, focus tooltips, andaria-pressedselection state. - Localizable labels:
featureLabelchanges presentation text (tooltips,aria-label) by stable code while canonical BCN names stay untouched in callbacks. - React first and SSR friendly: React owns the SVG tree (d3 only projects); regional components accept a
topologyoverride for server rendering, tests, and preloading. React 17-19. - Resilient partial data: units without values use
fillDefault; problems surface as deduplicated development-only warnings, never crashes or production noise. - Typed end to end:
RegionCode,ComunaCode,ProvinciaCode, opt-in regional data types, feature callbacks, scales, and every prop ship as TypeScript types.
npm install react-chile-mapThe national component keeps topology loading explicit so applications control when the larger national geometry is downloaded.
import { ChileMap } from "react-chile-map";
import regiones from "react-chile-map/data/regiones.json";
import type { Topology } from "topojson-specification";
<ChileMap
topology={regiones as unknown as Topology}
data={{ "13": 8_242_459, "05": 2_003_119 }}
colors={["#dbeafe", "#1e40af"]}
bins={6}
scaleType="quantile"
selected="13"
highlightMode="both"
onClick={(region) => console.log(region.code, region.name)}
tooltip={(region) => <strong>{region.name}</strong>}
formatValue={(value) => value.toLocaleString("es-CL")}
width={340}
height={720}
/>;RegionComunasMap loads the matching bundled topology from its region prop. Values are keyed by five digit comuna CUT codes.
import { RegionComunasMap } from "react-chile-map";
<RegionComunasMap
region="13"
data={{ "13101": 100, "13114": 82, "13201": 91 }}
onClick={(comuna) => {
console.log(comuna.code);
console.log(comuna.region);
console.log(comuna.province, comuna.provinceName);
}}
loadingFallback={<p>Loading comunas…</p>}
errorFallback={(error) => <p>{error.message}</p>}
onLoadError={(error) => reportError(error)}
/>;RegionProvinciasMap follows the same API and uses three digit provincia CUT codes.
import { RegionProvinciasMap } from "react-chile-map";
<RegionProvinciasMap
region="13"
data={{ "131": 100, "132": 78, "133": 52 }}
onHover={(provincia) => console.log(provincia?.region)}
/>;region is typed as the zero padded RegionCode union ("01" through "16"). Both regional components cache successful topology imports. Passing a new region ignores stale in flight results.
The bundled codes for all 16 regions are available as opt-in types. Use satisfies to catch unknown codes or codes from another region while preserving the flexible Record<string, number> component prop:
import type { ComunaData, ProvinciaData } from "react-chile-map";
const comunaData = {
"13101": 50,
"13113": 82,
} satisfies ComunaData<"13">;
const provinciaData = {
"131": 100,
"132": 78,
} satisfies ProvinciaData<"13">;
<RegionComunasMap region="13" data={comunaData} />;
<RegionProvinciasMap region="13" data={provinciaData} />;ComunaCode<R> and ProvinciaCode<R> expose the corresponding exact code unions. The opt-in types describe the bundled BCN data and do not restrict dynamic records or custom topology codes passed directly to the components.
Use topology to bypass the built in lazy loader:
<RegionComunasMap region="13" topology={preloadedTopology} data={data} />This supports SSR, tests, application level preloading, and custom cartography.
All components support:
- linear or quantile color scales;
- custom
colorForlogic; - localized presentation labels through
featureLabel, without changing canonical BCN names; - controlled selection;
- hover and click callbacks with level specific feature types;
- default or custom tooltips;
stroke,fill, orbothhighlight modes;- configurable missing data, border, highlight, and size props;
- automatic side insets for oceanic islands, or
oceanicIslands={false}for a mainland-only view.
Missing values are normal and render with fillDefault (#e2e8f0 by default). In development, the package emits one aggregated console.warn for data codes absent from the current topology and another for non finite values. Every finite value in data, including values for codes outside the current topology, participates in the scale domain.
Keep official names in the topology and resolve presentation text from stable codes:
const englishLabels: Record<string, string> = {
"13": "Santiago Metropolitan Region",
};
<ChileMap topology={regiones} featureLabel={(feature) => englishLabels[feature.code] ?? feature.name} />;featureLabel controls the default tooltip heading and each SVG path's aria-label. Callbacks still receive the unmodified official feature.name, so applications can change locale without replacing geometry.
interface MapFeature {
code: string;
name: string;
value?: number;
}
interface ProvinciaFeature extends MapFeature {
region: RegionCode;
}
interface ComunaFeature extends MapFeature {
region: RegionCode;
province: string;
provinceName: string;
}The rendering engine uses normalized parent metadata internally while callbacks expose level-specific semantic fields.
The map fills come from a shared scale descriptor. Build the same descriptor with scaleFromData and pass it to <Legend> so boundaries always match the map:
import { ChileMap, Legend, scaleFromData } from "react-chile-map";
const scale = scaleFromData(data, { colors, bins: 6, type: "quantile" });
<ChileMap topology={regiones} data={data} colors={colors} bins={6} scaleType="quantile" />
<Legend scale={scale} formatValue={(v) => v.toLocaleString("es-CL")} showMissing />scaleType accepts "linear", "quantile", or "threshold" (with an explicit thresholds array). The domain covers every finite value in data. Low-level helpers createScale, makePalette, quantize, and quantile are also exported. See API.md for the full prop-by-prop reference.
When onClick is provided, every unit is a role="button" path with a roving tab index: one Tab stop per map, arrow keys move between units, Enter/Space activate, focus shows the tooltip, and aria-pressed reflects the controlled selection.
npm install
npm run dev
npm run test # vitest suite (rendering, interactions, loaders)
npm run test:coverage # unit suite plus V8 coverage thresholds
npm run test:browser # Chromium keyboard and axe checks
npm run test:visual # deterministic cartography screenshots
npm run build-data # regenerate TopoJSON from the BCN shapefiles
npm run generate-data-types # regenerate opt-in regional code types
npm run validate-data # assert artifact counts, bounds, and size budgets
npm run validate-data-types # assert generated types match all regional data
npm run verify-package # pack + consumer fixtures (React 17/18/19)
npm run typecheck --workspace=react-chile-map
npm run build --workspace=react-chile-map
npm run build:demo
npm run build:docs # build the English and Spanish Docusaurus site
npm run dev:docs # preview both locales with a working language selector
npm run dev:docs:en # English-only hot reload
npm run dev:docs:es # Spanish-only hot reloadSource: Biblioteca del Congreso Nacional de Chile (BCN), mapas vectoriales.
The published TopoJSON covers the full national territory present in the BCN sources, including the Pacific islands (Rapa Nui, Juan Fernández). Because those islands lie thousands of kilometers west of the mainland, every component automatically renders polygons west of longitude -76.5 inside side inset boxes with their own projection - one box for Rapa Nui and one for the remaining Pacific islands. Insets appear only when the rendered topology contains oceanic geometry (the national map and Región de Valparaíso today); hover, click, selection, and choropleth colors work identically inside them.
Pass oceanicIslands={false} to render a mainland-only view: oceanic polygons are dropped, no inset column is reserved, and fully oceanic units (such as the Isla de Pascua comuna) keep their data but draw no shape.
Known exclusions:
- The BCN sources contain no Antarctic geometry, so the "Antártica" comuna and the "Antártica Chilena" provincia have no published polygons. The artifacts therefore contain 345 comunas and 56 provincias.
- Islets smaller than each layer's
minIslandAreapipeline threshold (for example Salas y Gómez, Santa Clara, and the Desventuradas) are removed to control package size. Their parent comunas remain present.
The source material is referential and should not be used where geodetic precision is required. Maps depicting Chilean limits and borders do not bind the State of Chile.