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

awesome-heatmap

v0.1.1

Published

Zero-dependency Heatmap Grid component for React — auto-extracts numeric matrices from any data shape, 8 color scales, 3 themes, IDB prefs persistence.

Readme

awesome-heatmap

A zero-dependency heatmap grid React component. Feed it any data shape — arrays of objects, REST API URLs, GeoJSON, CSV-style nested arrays — and it auto-extracts a numeric matrix, renders colour-coded cells, and gives you a full toolbar with colour scale picker, row sorting, cell size slider, theme switcher, and a floating tooltip. All styles are injected at runtime, so no CSS import is needed.


Features

  • Universal data ingestion — arrays of objects, URL strings (JSON / REST API), GeoJSON FeatureCollections, CSV-style [[headers], [row], ...], envelope-wrapped responses ({ data: [...] }, { results: [...] }, etc.)
  • Auto matrix extraction — detects numeric columns (≥ 75% parseable values), picks the first non-numeric column as the row label, flattens nested objects up to 3 levels deep
  • 8 colour scales — Plasma, Inferno, Viridis, Magma, Thermal, Ice, Red→Blue, Spectral — all using perceptually uniform control-point interpolation
  • 3 built-in themesdark, light, ember — all colours driven by CSS custom properties
  • Toolbar controls — colour scale dropdown with preview dots, row sort (original / A→Z / Z→A / sum↑ / sum↓), cell size slider (16–80px), show/hide cell values toggle, theme switcher
  • Column header mini-bars — each column header shows a colour-coded bar for its average value relative to the global range
  • Sticky row labels + column headers — both stay in view while scrolling a large matrix
  • Floating tooltip — shows row label, column label, formatted value, and a colour-matched progress bar
  • Summary footer — global Σ, x̄, max, min, and cell count
  • IndexedDB preference persistence — theme, scale, sort, cell size, and show-values are saved per storageKey and restored on next load
  • Self-contained styles — CSS Module replaced with a runtime-injected <style> tag; no stylesheet import required
  • ESM + CJS dual build — works in Vite, Next.js, CRA, and any modern bundler
  • Zero runtime dependencies beyond React

Installation

npm install awesome-heatmap
# or
yarn add awesome-heatmap
# or
pnpm add awesome-heatmap

Peer dependencies — React ≥ 18 and ReactDOM ≥ 18 must already be installed.


Quick Start

No CSS import needed — styles are injected automatically.

import { AwesomeHeatmap } from 'awesome-heatmap';

var data = [
  { city: 'New York',  jan: 2,  feb: 4,  mar: 10, apr: 17, may: 23, jun: 28 },
  { city: 'London',    jan: 5,  feb: 6,  mar: 9,  apr: 13, may: 17, jun: 20 },
  { city: 'Tokyo',     jan: 6,  feb: 7,  mar: 11, apr: 17, may: 22, jun: 26 },
  { city: 'Sydney',    jan: 26, feb: 26, mar: 23, apr: 18, may: 14, jun: 11 },
  { city: 'Dubai',     jan: 19, feb: 21, mar: 25, apr: 33, may: 38, jun: 40 },
];

export default function App() {
  return (
    <div style={{ width: '100%', height: '500px' }}>
      <AwesomeHeatmap
        data={data}
        title="Monthly Temperatures (°C)"
      />
    </div>
  );
}

Props

| Prop | Type | Default | Description | |---|---|---|---| | data | Array \| string | [] | Data to visualise. Pass an array of objects, or a URL string pointing to a JSON endpoint. See Data formats below. | | title | string | 'Heatmap' | Title shown in the toolbar. | | theme | 'dark' \| 'light' \| 'ember' | 'dark' | Initial colour theme. User can switch at runtime via the toolbar. | | colorScale | string | 'plasma' | Initial colour scale key. Must be one of the COLOR_SCALES keys. | | storageKey | string | 'awesome-heatmap' | IndexedDB key used to persist and restore user preferences. Pass a unique value per instance. Set to '' to disable persistence. | | height | string \| number | '100%' | CSS height of the component. Numbers are treated as px. | | onThemeChange | (theme: string) => void | — | Called whenever the user switches theme, with the new theme name. |


Named Exports

COLOR_SCALES

An object mapping scale keys to display labels. Use this to build your own scale picker or to pass a valid value to the colorScale prop.

import { COLOR_SCALES } from 'awesome-heatmap';

console.log(COLOR_SCALES);
// {
//   plasma:   "Plasma",
//   inferno:  "Inferno",
//   viridis:  "Viridis",
//   magma:    "Magma",
//   thermal:  "Thermal",
//   ice:      "Ice",
//   rdbu:     "Red → Blue",
//   spectral: "Spectral",
// }

PAGE_BG

An object mapping theme names to their recommended outer page background colours. Useful for styling the page wrapper to match the heatmap.

import { PAGE_BG } from 'awesome-heatmap';

console.log(PAGE_BG);
// { dark: "#04060c", light: "#e0e5f0", ember: "#070402" }

Data Formats

AwesomeHeatmap accepts data in many shapes and normalises them automatically.

Array of objects (recommended)

var data = [
  { product: 'Shoes',  mon: 120, tue: 95,  wed: 143 },
  { product: 'Bags',   mon: 88,  tue: 112, wed: 76  },
  { product: 'Hats',   mon: 54,  tue: 67,  wed: 91  },
];

The first non-numeric column (product) becomes the row label. All numeric columns become the heatmap columns.

URL string — JSON endpoint

<AwesomeHeatmap data="https://api.example.com/metrics" title="Server Metrics" />

The component fetches the URL, then unwraps common envelope keys (data, results, items, rows, etc.) automatically.

Envelope-wrapped response

{ "results": [ { "region": "APAC", "q1": 4.2, "q2": 5.1, "q3": 3.8 } ] }

CSV-style nested arrays (first row = headers)

var data = [
  ['city',      'q1',  'q2',  'q3',  'q4'],
  ['Berlin',    8.2,   14.1,  19.3,  9.5 ],
  ['Paris',     9.1,   15.0,  20.2,  10.1],
  ['Madrid',    11.5,  17.3,  24.8,  13.2],
];

GeoJSON FeatureCollection

var geojson = {
  type: 'FeatureCollection',
  features: [
    { type: 'Feature', geometry: { type: 'Point', coordinates: [0,0] }, properties: { name: 'A', value: 42 } },
  ],
};

Properties are extracted automatically; the geometry is ignored.


Examples

Example 1 — Dark theme with Viridis scale and persistence disabled

import { AwesomeHeatmap } from 'awesome-heatmap';

var salesData = [
  { rep: 'Alice',   jan: 42000, feb: 38000, mar: 51000, apr: 47000, may: 55000, jun: 61000 },
  { rep: 'Bob',     jan: 31000, feb: 44000, mar: 39000, apr: 52000, may: 48000, jun: 43000 },
  { rep: 'Carol',   jan: 58000, feb: 62000, mar: 55000, apr: 67000, may: 71000, jun: 69000 },
  { rep: 'David',   jan: 27000, feb: 31000, mar: 35000, apr: 29000, may: 38000, jun: 42000 },
  { rep: 'Eva',     jan: 49000, feb: 53000, mar: 61000, apr: 58000, may: 64000, jun: 72000 },
  { rep: 'Frank',   jan: 36000, feb: 41000, mar: 38000, apr: 44000, may: 46000, jun: 50000 },
];

export default function SalesDashboard() {
  return (
    <div style={{ width: '100%', height: '480px', borderRadius: 16, overflow: 'hidden' }}>
      <AwesomeHeatmap
        data={salesData}
        title="Sales by Rep — H1"
        theme="dark"
        colorScale="viridis"
        storageKey=""
      />
    </div>
  );
}

Example 2 — Light theme, live data from a REST API, onThemeChange callback

import { useState } from 'react';
import { AwesomeHeatmap, PAGE_BG } from 'awesome-heatmap';

export default function LiveMetrics() {
  var [theme, setTheme] = useState('light');

  return (
    <div style={{ background: PAGE_BG[theme], minHeight: '100vh', padding: 32, transition: 'background 0.4s' }}>
      <div style={{ width: '100%', height: '600px', borderRadius: 20, overflow: 'hidden', boxShadow: '0 4px 24px rgba(0,0,0,0.12)' }}>
        <AwesomeHeatmap
          data="https://jsonplaceholder.typicode.com/users"
          title="User Metrics (live)"
          theme={theme}
          colorScale="thermal"
          storageKey="live-metrics-heatmap"
          onThemeChange={function(t) { setTheme(t); }}
        />
      </div>
    </div>
  );
}

Example 3 — Ember theme, CSV-style array, controlled height, multiple instances

import { AwesomeHeatmap, COLOR_SCALES } from 'awesome-heatmap';

var weeklyTraffic = [
  ['page',       'mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'],
  ['Home',       9420,  8830,  9210,  9640,  11200, 8100,  7300],
  ['Products',   4210,  4650,  4120,  4980,  5340,  3800,  3200],
  ['Blog',       2100,  2340,  2890,  2670,  2450,  3100,  2900],
  ['Pricing',    1840,  2010,  1950,  2200,  2680,  1400,  1100],
  ['Contact',    820,   910,   880,   970,   1050,  640,   580 ],
  ['Docs',       3400,  3800,  4200,  4100,  3900,  2200,  1900],
];

var teamPerformance = [
  ['team',       'sprints_completed', 'bugs_closed', 'prs_merged', 'reviews_done', 'deploys'],
  ['Frontend',   8,   24,  31,  42,  12],
  ['Backend',    6,   38,  22,  28,   9],
  ['Mobile',     7,   19,  17,  24,   6],
  ['DevOps',     5,   11,   8,  15,  28],
  ['Data',       4,   14,  12,  18,   4],
];

export default function MultiHeatmap() {
  return (
    <div style={{ background: '#070402', minHeight: '100vh', padding: 32, display: 'flex', flexDirection: 'column', gap: 24 }}>

      <div style={{ height: 420, borderRadius: 16, overflow: 'hidden' }}>
        <AwesomeHeatmap
          data={weeklyTraffic}
          title="Weekly Page Traffic"
          theme="ember"
          colorScale="plasma"
          storageKey="traffic-heatmap"
        />
      </div>

      <div style={{ height: 360, borderRadius: 16, overflow: 'hidden' }}>
        <AwesomeHeatmap
          data={teamPerformance}
          title="Team Performance — Current Sprint"
          theme="ember"
          colorScale="inferno"
          storageKey="team-heatmap"
        />
      </div>

      {/* Display available colour scales */}
      <div style={{ color: '#a0836a', fontFamily: 'monospace', fontSize: 12 }}>
        Available scales: {Object.keys(COLOR_SCALES).join(', ')}
      </div>
    </div>
  );
}

No separate CSS file is produced. All styles are bundled as a JavaScript string and injected into <head> when the component first mounts.

Toolbar Controls

| Control | Description | |---|---| | Cell size slider | Resize all cells from 16px to 80px. Values are shown inside cells when size ≥ 28px and the eye toggle is on. | | Eye toggle | Show or hide numeric values inside each cell. | | Colour scale dropdown | Switch between the 8 available scales. Each option shows a preview strip. | | Sort dropdown | Sort rows by: original order, label A→Z / Z→A, or row sum ascending / descending. | | Theme dots | Switch between Dark (blue), Light (indigo), and Ember (orange) themes. |


CSS Custom Properties

All visual tokens are CSS custom properties set on the root element. Override them per-instance via a wrapper class:

.my-heatmap-wrapper {
  --hm-bg:        #0a0a0a;
  --hm-surface:   #141414;
  --hm-surface2:  #1c1c1c;
  --hm-surface3:  #242424;
  --hm-border:    rgba(255,255,255,0.06);
  --hm-border2:   rgba(255,255,255,0.12);
  --hm-text1:     #f0f0f0;
  --hm-text2:     #888;
  --hm-text3:     #444;
  --hm-accent:    #7c3aed;
  --hm-accentbg:  rgba(124,58,237,0.12);
  --hm-shadow:    0 8px 40px rgba(0,0,0,0.7);
}

Then wrap the component:

<div className="my-heatmap-wrapper" style={{ height: 500 }}>
  <AwesomeHeatmap data={data} />
</div>

Preference Persistence

User preferences (theme, colour scale, sort order, cell size, show-values toggle) are automatically saved to IndexedDB under the storageKey and restored on the next mount. To use multiple independent instances on the same page, pass a unique storageKey to each:

<AwesomeHeatmap data={salesData}    storageKey="sales-heatmap" />
<AwesomeHeatmap data={trafficData}  storageKey="traffic-heatmap" />

To disable persistence entirely, pass an empty string:

<AwesomeHeatmap data={data} storageKey="" />

License

MIT