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

react-apexmaps

v0.3.0

Published

React component for ApexMaps

Readme

react-apexmaps

React component for ApexMaps.

npm install apexmaps react-apexmaps

apexmaps and react are peer dependencies, so there is one copy of each in your app.

Quick start

import ApexMaps from 'react-apexmaps'
import 'apexmaps/apexmaps.css'

const data = [
  { key: 'IND', value: 42 },
  { key: 'BRA', value: 71 },
]

export default function Coverage() {
  return (
    <ApexMaps
      options={{ geo: { map: 'world' } }}
      series={[
        { type: 'choropleth', name: 'Coverage', data, scale: { palette: 'blues', classes: 5 } },
      ]}
      onFeatureClick={({ key }) => console.log(key)}
      height={480}
    />
  )
}

Props

| Prop | Type | Notes | | --- | --- | --- | | options | ApexMapsOptions | Required. The same options object the core takes. | | series | Series[] | Optional shorthand for options.series. Takes precedence over it. | | width / height | number \| string | Shorthand for options.chart.width / .height. See sizing. | | mapRef | { current: ApexMaps \| null } | Receives the live instance. Nulled on unmount. | | on* | function | One prop per core event, listed below. | | anything else | | Forwarded to the outer <div>: className, style, id, aria-*. |

Every core event has a prop: onRendered, onUpdated, onResized, onFeatureClick, onFeatureHover, onFeatureFocus, onMarkClick, onMarkHover, onClusterClick, onDrilldown, onDrillup, onSelectionChange, onLegendToggle, onZoom, onPanEnd. Each receives the payload the core emits, fully typed. Handlers are read at emit time, so an inline arrow function costs nothing and never misses an event.

Sizing

Use the width and height props, not CSS. The map's height comes from options.chart.height, which defaults to 400, and an explicit number wins over the container. So a style={{ height: 600 }} on its own gives you a 600px box with a 400px map in it.

Width behaves differently, because chart.width defaults to '100%': it does follow the container, and keeps following it as the container resizes.

To have the height follow the container too, say so:

<ApexMaps options={options} height="100%" style={{ height: '60vh' }} />

Both props accept a number (pixels) or a CSS string, and a percentage string is what puts the dimension under the container's control.

The imperative API

Camera moves, drilldown, export and diagnostics are methods rather than options, because they are actions rather than state. Reach them through mapRef:

const map = useRef(null)

<ApexMaps mapRef={map} options={options} />
<button onClick={() => map.current?.frameFeature('IND')}>Zoom to India</button>
<button onClick={() => map.current?.exportPNG()}>Download</button>

What it does with your props

React hands the component a brand new options object every time the parent renders, so the component compares deeply rather than by reference. Four rules in that comparison are worth knowing, because each one is a deliberate trade:

Functions compare by source, not identity. An inline formatter is a new function on every render but usually the same behaviour, and treating that as a change would make every render a redraw. Editing the formatter's source is noticed; a formatter that reads changed state through its closure is not, so lift such values into options where they are data.

geo.map compares by identity and is never walked. Object geometry can be megabytes, and deep comparing it every render would cost more than the redraw it avoids. Pass a stable reference (a module-level constant, useMemo, or a pack id string). Rebuilding an equal topology object inline on every render will redraw on every render.

Series-only changes go to updateSeries, which tweens. Everything else goes to updateOptions, which redraws. This holds whether the data is in the series prop or in options.series.

A map or projection change reprojects; nothing else does. Camera position, selection and drilldown depth survive an options change, so a parent re-render does not throw away where the reader had navigated to.

One thing to watch

updateOptions merges, so removing a key from options does not reset it. This is core behaviour rather than something the wrapper adds, and it does not fit the declarative style:

// Turning `show` off leaves the labels on, because `dataLabels` merely disappears.
options={{ ...base, ...(show && { dataLabels: { enabled: true } }) }}

// Pass the value instead of omitting the key.
options={{ ...base, dataLabels: { enabled: show } }}

Server rendering

The component is safe to import and render on the server: it emits its container and does nothing else, because everything that touches a document happens in an effect. It hydrates and then builds the map on the client.

It is not server rendered. The core has no HTML output path yet, so there is no markup to send down ahead of hydration, and there is no react-apexmaps/server entry rather than one that returns an empty box.

The DOM it renders

Two nested <div>s. React owns the outer one and applies your className, style and other attributes to it. ApexMaps owns the inner one and writes its own class and custom properties there.

They are separate on purpose: React applies className by replacing the whole attribute, so if the two shared an element then changing className would delete the apexmaps class and every style rule in the package would stop matching, leaving a map that renders correctly once and then silently loses all of its styling.