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

ethio-map-kit

v0.1.5

Published

Type-safe React components for an interactive Ethiopia regional SVG map.

Readme

Ethio Map Kit

React components for building interactive Ethiopia map visualizations.

Ethio Map Kit includes a regional Ethiopia map, smooth region selection, light/dark themes, terrain and heat layers, marker support, service stats, and TypeScript types.

Install

npm install ethio-map-kit

Import the stylesheet once:

import "ethio-map-kit/styles.css";

Quick Start

import {
  EthiopiaMap,
  defaultRegionData,
  defaultRegionServiceStats,
  defaultLocations,
} from "ethio-map-kit";
import "ethio-map-kit/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);
      }}
    />
  );
}

Terrain Layer

<EthiopiaMap
  data={defaultRegionData}
  layer="terrain"
  theme="dark"
/>

The bundled terrain image is a transparent WebP, so it works in both light and dark mode.

Custom Data

import type { RegionRecord, RegionDatum } from "ethio-map-kit";

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

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 "ethio-map-kit";

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