ethiopia-map
v0.1.6
Published
Interactive Ethiopian map components for React and Next.js applications.
Maintainers
Readme
Ethiopian Maps - React Ethiopia Map Component
Interactive Ethiopian map components for React and Next.js applications.
Build dashboards, data visualizations, and applications using an interactive Ethiopia regional map with SVG regions, markers, themes, and TypeScript support.
React Ethiopia Map
Ethiopian Maps provides an interactive Ethiopia map component for React and Next.js applications.
It includes:
- Ethiopian regional map
- SVG-based map rendering
- Region selection
- Data visualization
- Markers
- Custom themes
- TypeScript support
Install
npm install ethiopia-mapImport the stylesheet once:
import "ethiopia-map/styles.css";Quick Start
import {
EthiopiaMap,
defaultRegionData,
defaultRegionServiceStats,
defaultLocations,
} from "ethiopia-map";
import "ethiopia-map/styles.css";
export function App() {
return (
<EthiopiaMap
data={defaultRegionData}
serviceStats={defaultRegionServiceStats}
locations={defaultLocations}
theme="light"
layer="regions"
showLabels
onRegionSelect={({ regionId, region }) => {
console.log(regionId, region);
}}
/>
);
}Interactive Ethiopia Map Layers
<EthiopiaMap
data={defaultRegionData}
layer="terrain"
theme="dark"
/>The bundled terrain layer uses a transparent WebP image so it renders cleanly in both light and dark themes.
Custom Data
import type { RegionRecord, RegionDatum } from "ethiopia-map";
const data: RegionRecord<RegionDatum> = {
...defaultRegionData,
amhara: {
name: "Amhara",
value: 78,
trend: "+12%",
copy: "High regional index with several sample cities.",
},
};
<EthiopiaMap data={data} />;Common Props
| Prop | Type | Description |
| --- | --- | --- |
| data | RegionRecord<RegionDatum> | Region values and copy keyed by region ID. |
| serviceStats | RegionServiceStats | Optional service indicators for each region. |
| locations | LatLngLocation[] | Optional marker/location data. |
| theme | "light" \| "dark" | Map theme. |
| layer | "regions" \| "heat" \| "terrain" | Active visual layer. |
| defaultLayer | "regions" \| "heat" \| "terrain" | Initial visual layer for uncontrolled maps. |
| onLayerChange | (layer) => void | Called when the built-in layer control requests a layer change. |
| viewMode | "map" \| "bars" | Map view or bar view. |
| selectionAnimation | "pulse" \| "static" \| "none" \| "outline" | Selected-region animation style. |
| showLabels | boolean | Show or hide region labels. |
| showMarkers | boolean | Show or hide location markers. |
| showInMapStats | boolean | Show or hide the selected region service overlay inside the map surface. |
| regionColors | PartialRegionRecord<string> | Override region colors. |
| selectedRegionId | EthiopiaRegionId \| null | Controlled selected region. |
| defaultSelectedRegionId | EthiopiaRegionId \| null | Initial selected region. |
| onRegionSelect | (selection) => void | Called when a region is selected or cleared. |
| className | string | Class applied to the map surface in the all-in-one EthiopiaMap. |
| style | React.CSSProperties | Inline style applied to the map surface in the all-in-one EthiopiaMap. Useful for height and width. |
| terrainImageUrl | string | Custom terrain image URL. |
| showLayerControl | boolean | Show a bottom-left Normal/Terrain map toggle inside the map surface. |
| layerControlClassName | string | Extra class for the built-in layer toggle. |
| layerControlLabels | { regions?: string; terrain?: string } | Custom labels for the built-in layer toggle. |
Map Eras & Year Selection
EthiopiaMap and EthiopiaMapProvider support administrative boundary map eras from 1995 to Present across 4 historical eras.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| era | "11-region" \| "12-region" \| "13-region" \| "14-region" | "14-region" | Explicitly select map administrative boundary era. |
| year | number \| string | undefined | Automatically resolves the appropriate map era for any given year. |
Historical Map Eras
| Era (era) | Applicable Years (year) | Units | Administrative Boundary Changes |
| --- | --- | --- | --- |
| "11-region" | 1995 – 2019 (year < 2020) | 11 units | Tigray, Afar, Amhara, Oromia, Somali, Benishangul-Gumuz, Gambela, SNNPR, Harari, Addis Ababa, Dire Dawa. |
| "12-region" | 2020 (year < 2021) | 12 units | Sidama established as a separate regional state from SNNPR. |
| "13-region" | 2021 – 2022 (year < 2023) | 13 units | South West Ethiopia Peoples' Region established as a separate regional state from SNNPR. |
| "14-region" | 2023 – Present (year >= 2023) | 14 units | SNNPR reorganized into South Ethiopia and Central Ethiopia regional states. |
Code Examples
Explicit Era Prop:
import { EthiopiaMap } from "ethiopia-map";
import "ethiopia-map/styles.css";
// 1995–2020 map (11 units)
<EthiopiaMap era="11-region" data={regionData} />
// 2021–2022 map (13 units)
<EthiopiaMap era="13-region" data={regionData} />Automatic Year Resolution:
// Resolves to 11-region era (year < 2020)
<EthiopiaMap year={2018} data={regionData} />
// Resolves to 12-region era (Sidama formed)
<EthiopiaMap year={2020} data={regionData} />
// Resolves to 13-region era (South West formed)
<EthiopiaMap year={2022} data={regionData} />
// Resolves to 14-region era (2023–Present)
<EthiopiaMap year={2024} data={regionData} />Composable Provider Example:
<EthiopiaMapProvider
data={regionData}
serviceStats={serviceStats}
era="13-region" // or year={2022}
>
<EthiopiaMapSurface />
<EthiopiaMapLegend />
</EthiopiaMapProvider>In-Map Layer Toggle
Use showLayerControl when you want a small Normal/Terrain switch inside the map, similar to common map viewers.
<EthiopiaMap
data={defaultRegionData}
defaultLayer="regions"
showLayerControl
/>For a controlled map, keep your own layer state and handle onLayerChange:
const [layer, setLayer] = useState<EthiopiaMapLayer>("regions");
<EthiopiaMap
data={defaultRegionData}
layer={layer}
onLayerChange={setLayer}
showLayerControl
/>;Sizing The Map
The map surface fills its own container and has a default minimum height. Use className, style, or your layout wrapper to control the rendered size.
<EthiopiaMap
data={defaultRegionData}
serviceStats={defaultRegionServiceStats}
className="w-full"
style={{ width: "100%", height: 640, minHeight: 640 }}
/>With composable components, size EthiopiaMapSurface directly:
<EthiopiaMapProvider data={defaultRegionData}>
<div className="grid gap-4 lg:grid-cols-[minmax(0,1fr)_320px]">
<EthiopiaMapSurface
className="w-full"
style={{ height: 620, minHeight: 620 }}
/>
<EthiopiaSelectedRegionCard />
</div>
</EthiopiaMapProvider>Composable Components
For custom layouts, wrap components with EthiopiaMapProvider.
import {
EthiopiaMapProvider,
EthiopiaMapSurface,
EthiopiaMapLegend,
EthiopiaSelectedRegionCard,
EthiopiaRegionServicesCard,
EthiopiaRegionServicesPanel,
EthiopiaLocationsPanel,
defaultRegionData,
defaultRegionServiceStats,
} from "ethiopia-map";
export function Dashboard() {
return (
<EthiopiaMapProvider
data={defaultRegionData}
serviceStats={defaultRegionServiceStats}
>
<EthiopiaMapSurface />
<EthiopiaMapLegend />
<EthiopiaSelectedRegionCard />
<EthiopiaRegionServicesCard />
<EthiopiaLocationsPanel />
</EthiopiaMapProvider>
);
}Service Stats Layouts
Service stats can appear inside the map surface, outside the map, or in a standalone details page. Use either the in-map overlay or the external card unless you intentionally want both.
In-map overlay only:
<EthiopiaMapProvider
data={defaultRegionData}
serviceStats={defaultRegionServiceStats}
showInMapStats
>
<EthiopiaMapSurface />
<EthiopiaSelectedRegionCard />
</EthiopiaMapProvider>External service card only:
<EthiopiaMapProvider
data={defaultRegionData}
serviceStats={defaultRegionServiceStats}
showInMapStats={false}
>
<div className="grid gap-4 lg:grid-cols-[minmax(0,1fr)_320px]">
<EthiopiaMapSurface className="w-full" style={{ height: 620 }} />
<EthiopiaRegionServicesCard />
</div>
</EthiopiaMapProvider>Customize the in-map overlay and its scroll list from EthiopiaMapSurface:
<EthiopiaMapSurface
inMapStatsClassName="my-map-stats"
inMapStatsStyle={{ maxHeight: 320 }}
inMapStatsListClassName="my-map-stats-list"
inMapStatsListStyle={{ maxHeight: 240 }}
/>Opt in to incremental row rendering when a region has many indicators:
<EthiopiaRegionServicesCard
incrementalRows={{
enabled: true,
initialCount: 8,
step: 8,
thresholdPx: 240,
}}
/>The same option works on the in-map overlay:
<EthiopiaMapSurface
incrementalRows={{
enabled: true,
initialCount: 8,
step: 8,
thresholdPx: 320,
showMoreFallback: false,
animateNewRows: false,
sentinelVisible: false,
}}
/>incrementalRows is client-side only. Pass the full service list, and the component reveals more rows from that already-loaded data as the user scrolls. For compact in-map overlays, keep animateNewRows false and use a larger thresholdPx so rows appear before the user reaches the end of the visible list.
For server-rendered detail pages, fetch data in your app and pass it directly to the context-free panel. The package does not include Prisma, Next.js caching, route generation, ISR, or database fetching.
const services = await getCachedMapsData("afar", 2021, true);
return (
<EthiopiaRegionServicesPanel
regionName="Afar"
regionValue={34}
services={services.afar ?? []}
/>
);Exports
EthiopiaMapEthiopiaMapProviderEthiopiaMapSurfaceEthiopiaMapLegendEthiopiaSelectedRegionCardEthiopiaRegionServicesCardEthiopiaRegionServicesPanelEthiopiaLocationsPaneluseEthiopiaMapuseSelectedRegionIddefaultRegionDatadefaultRegionServiceStatsdefaultLocationsETHIOPIA_REGION_IDSETHIOPIA_AUTO_TOUR_SEQUENCE
Region IDs
tigray
afar
amhara
benishangul-gumuz
gambela
south-west-ethiopia
central-ethiopia
sidama
south-ethiopia
oromia
addis-ababa
somali
dire-dawa
harari