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

@meetreeve/maps-react

v0.1.0

Published

Reeve Maps React components and API clients

Downloads

194

Readme

@reeve/maps-react

The Reeve Maps frontend SDK. Provides typed API clients (geocode, getMapsConfig, fetchStaticImageBlob) that talk to your app's backend proxy of the reeve-services maps API, a choroplethSpec utility for computing Mapbox paint expressions and legend items, a <StaticMapImage> component for server-rendered or non-interactive map tiles, an interactive <ReeveMap> wrapper over react-map-gl/mapbox-gl with <ChoroplethLayer> and <HeatmapLayer> children, and a computeBounds helper for deriving fitBounds from a GeoJSON FeatureCollection.


Install

pnpm add @reeve/maps-react

Peer dependencies

pnpm add react react-dom mapbox-gl@^3 react-map-gl@^8

You must also import the Mapbox CSS once in your app (e.g. in your root layout or global CSS entry):

import "mapbox-gl/dist/mapbox-gl.css";

Next.js: SSR note

<ReeveMap> renders react-map-gl / mapbox-gl, which touch window at import time and cannot run on the server. Wrap it with next/dynamic:

// components/MapShell.tsx  (client component file)
import dynamic from "next/dynamic";

const ReeveMap = dynamic(
  () => import("@reeve/maps-react").then((m) => ({ default: m.ReeveMap })),
  { ssr: false }
);

// — or mark your whole component "use client" and lazy-import at the top:
// "use client";
// const ReeveMap = dynamic(..., { ssr: false });

<StaticMapImage> does not depend on mapbox-gl and can be used freely in server or client components.


API clients

All three clients accept a base string that points at your app's own backend proxy of the reeve-services maps API (e.g. /api/maps/v1). The proxy forwards requests to reeve-services over the host key — credentials never reach the browser.

getMapsConfig(base, init?)

Fetches the Mapbox public token, default style, center, and zoom from the backend.

import { getMapsConfig } from "@reeve/maps-react";

const config = await getMapsConfig("/api/maps/v1");
// { style, public_token, default_center, default_zoom, attribution }

geocode(base, body, init?)

Forward-geocodes an address or place name.

import { geocode } from "@reeve/maps-react";

const { results } = await geocode("/api/maps/v1", {
  query: "123 Main St, Austin, TX",
  country: "US",
  limit: 5,
});
// results: Array<{ lat, lng, label, relevance? }>

fetchStaticImageBlob(base, params, init?)

Returns a Blob (PNG) for a static map image. Renders server-side via the reeve-services static-image endpoint.

import { fetchStaticImageBlob } from "@reeve/maps-react";

const blob = await fetchStaticImageBlob("/api/maps/v1", {
  size: [800, 400],
  center: [-97.7431, 30.2672],
  zoom: 10,
  pins: [{ lng: -97.7431, lat: 30.2672, color: "#E63946", label: "A" }],
  retina: true,
});
const url = URL.createObjectURL(blob);

<StaticMapImage>

Fetches and renders a static map image as an <img>. Manages the object URL lifecycle internally.

import { StaticMapImage } from "@reeve/maps-react";

<StaticMapImage
  base="/api/maps/v1"
  size={[640, 320]}
  center={[-97.7431, 30.2672]}
  zoom={10}
  alt="Austin overview"
  className="rounded-lg"
/>

Props extend StaticImageParams (all fetchStaticImageBlob params) plus:

| Prop | Type | Default | Description | |------|------|---------|-------------| | base | string | — | Backend proxy base URL | | alt | string | "Map" | <img> alt text | | className | string | — | CSS class on the container/img | | fetchInit | RequestInit | — | Passed to fetch (headers, signal, etc.) |


<ReeveMap> with <ChoroplethLayer>

Interactive Mapbox map. The token can be passed directly or resolved automatically from configUrl.

"use client";
import dynamic from "next/dynamic";
import { computeBounds, choroplethSpec } from "@reeve/maps-react";
import type { FeatureCollection } from "geojson";

const ReeveMap = dynamic(
  () => import("@reeve/maps-react").then((m) => ({ default: m.ReeveMap })),
  { ssr: false }
);
const ChoroplethLayer = dynamic(
  () => import("@reeve/maps-react").then((m) => ({ default: m.ChoroplethLayer })),
  { ssr: false }
);

// --- in your component ---
const geojson: FeatureCollection = /* your data, each feature has properties.value */;
const values = geojson.features.map((f) => f.properties?.value as number);

// Compute legend (same math as reeve-services BE — FE and BE classifications agree)
const { legend } = choroplethSpec(values, { scheme: "YlOrRd", classes: 5 });

// Fit map to data bounds
const bounds = computeBounds(geojson); // BBox | null

return (
  <div style={{ height: 500 }}>
    <ReeveMap
      configUrl="/api/maps/v1"
      fitBounds={bounds}
      interactiveLayerIds={["districts-fill"]}
      onFeatureClick={(feature) => console.log(feature)}
    >
      <ChoroplethLayer
        id="districts"
        data={geojson}
        property="value"
        values={values}
        scheme="YlOrRd"
        classes={5}
      />
    </ReeveMap>

    {/* Legend */}
    <ul>
      {legend.map((item) => (
        <li key={item.label} style={{ color: item.color }}>{item.label}</li>
      ))}
    </ul>
  </div>
);

ReeveMapProps

| Prop | Type | Default | Description | |------|------|---------|-------------| | token | string | — | Mapbox public token (pk.*). If omitted, fetched via configUrl. | | configUrl | string | — | Backend proxy base; calls GET {configUrl}/config for token + style. | | style | string | "mapbox://styles/mapbox/dark-v11" | Mapbox style URL | | style_ | CSSProperties | — | Inline style on the container div (named style_ to avoid clash with map style) | | initialViewState | { longitude, latitude, zoom } | — | Starting viewport | | fitBounds | BBox \| null | — | [[minLng,minLat],[maxLng,maxLat]] fit on mount | | fitPadding | number | 40 | Padding (px) around fitBounds | | interactiveLayerIds | string[] | — | Layer ids to enable hover/click events on | | onFeatureClick | (feature: unknown) => void | — | Called with the topmost feature on click | | onFeatureHover | (feature: unknown \| null) => void | — | Called with hovered feature (or null on leave) | | className | string | — | CSS class on the container div | | children | ReactNode | — | <Source>/<Layer> or <ChoroplethLayer>/<HeatmapLayer> |

ChoroplethLayerProps

| Prop | Type | Default | Description | |------|------|---------|-------------| | id | string | — | ID prefix; fill layer gets id {id}-fill | | data | FeatureCollection | — | GeoJSON source data | | property | string | "value" | Feature property to classify | | values | number[] | — | All numeric values (used to compute breaks) | | method | "quantile" \| "equal_interval" | "quantile" | Break classification method | | classes | number | 5 | Number of classes | | scheme | string | "YlOrRd" | Color scheme: "YlOrRd", "Blues", or "Gold" | | breaks | number[] | — | Explicit interior break values (overrides method/classes) | | opacity | number | 0.7 | Fill opacity |

HeatmapLayerProps

| Prop | Type | Default | Description | |------|------|---------|-------------| | id | string | — | ID prefix; heatmap layer gets id {id}-heatmap | | data | FeatureCollection | — | GeoJSON source data | | weightProperty | string | "weight" | Feature property used as heatmap weight | | intensity | number | 1 | Heatmap intensity multiplier | | radius | number | 15 | Heatmap radius (px) |


choroplethSpec and classBreaks

Use these to compute legend items or Mapbox paint expressions outside of <ChoroplethLayer>.

import { choroplethSpec, classBreaks, SCHEMES } from "@reeve/maps-react";

// Compute breaks only
const breaks = classBreaks(values, { method: "quantile", classes: 5 });

// Compute paint expression + legend
const { paintExpression, legend } = choroplethSpec(values, {
  method: "quantile",
  classes: 5,
  scheme: "Blues",
  property: "price", // feature property key
});

The break math (quantile and equal-interval) mirrors the reeve-services backend (choropleth.py) exactly — FE and BE classifications agree, so legend items rendered here match any server-side choropleth outputs.

Available schemes: "YlOrRd" (yellow-orange-red), "Blues", "Gold" (price ramp).


computeBounds

Derives a BBox from any FeatureCollection (supports Point, LineString, Polygon, Multi*, GeometryCollection). Returns null for an empty collection.

import { computeBounds } from "@reeve/maps-react";
import type { BBox } from "@reeve/maps-react";

const bounds: BBox | null = computeBounds(geojson);
// bounds: [[minLng, minLat], [maxLng, maxLat]]

Pass the result directly to <ReeveMap fitBounds={bounds}>.