npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ethiopia-map

v0.1.6

Published

Interactive Ethiopian map components for React and Next.js applications.

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-map

Import 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

  • EthiopiaMap
  • EthiopiaMapProvider
  • EthiopiaMapSurface
  • EthiopiaMapLegend
  • EthiopiaSelectedRegionCard
  • EthiopiaRegionServicesCard
  • EthiopiaRegionServicesPanel
  • EthiopiaLocationsPanel
  • useEthiopiaMap
  • useSelectedRegionId
  • defaultRegionData
  • defaultRegionServiceStats
  • defaultLocations
  • ETHIOPIA_REGION_IDS
  • ETHIOPIA_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