# Next.js Heatmap Chart

A Next.js heatmap chart for the App Router: an ApexCharts client component in AdminKit PRO that shades a grid of cells by value, with theme color ranges.

> This component is only available in **AdminKit PRO**. [Learn more](/pricing/#nextjs).

A heatmap uses color the way a bar chart uses height. Each cell of the grid is shaded by its value, so dense and quiet areas show up as dark and light patches. In [AdminKit for Next.js](/nextjs/), the `ApexChart` client component draws it with [ApexCharts](https://apexcharts.com/). The component is described in [Chart setup](/nextjs/docs/charts/overview/#apexcharts).

The chart draws in the browser only, while your page is a server component that renders on the server. A server component cannot pass the `options` function to a client component. Write the chart as a client component file, one that starts with `"use client"`, and render that from the page. The server render, and the static HTML of the export, contain a skeleton of the chart's size.

## Example

```html
<div id="apexcharts-heatmap"></div>

<script>
function generateData(count, yrange) {
  var data = [];
  for (var i = 0; i < count; i++) {
    data.push({
      x: (i + 1).toString(),
      y: Math.floor(Math.random() * (yrange.max - yrange.min + 1)) + yrange.min
    });
  }
  return data;
}

var series = [];
for (var i = 0; i < 9; i++) {
  series.push({ name: "Metric" + (i + 1), data: generateData(20, { min: 0, max: 90 }) });
}

var options = {
  chart: {
    height: 350,
    type: "heatmap",
  },
  dataLabels: {
    enabled: false
  },
  colors: ["#008FFB"],
  series: series,
  xaxis: {
    type: "category",
  }
}
adminkit.apexcharts("#apexcharts-heatmap", function () {
  return options;
});
</script>
```
```tsx code title="src/components/metrics-heatmap.tsx"
"use client";

import { ApexChart } from "@/components/plugins/apexcharts";

const generateData = (count: number, yrange: { min: number; max: number }) =>
  Array.from({ length: count }, (_, i) => ({
    x: (i + 1).toString(),
    y: Math.floor(Math.random() * (yrange.max - yrange.min + 1)) + yrange.min,
  }));

// Nine rows of 20 random values, generated once when the module loads
const series = Array.from({ length: 9 }, (_, i) => ({
  name: `Metric${i + 1}`,
  data: generateData(20, { min: 0, max: 90 }),
}));

export function MetricsHeatmap() {
  return (
    <div className="position-relative" style={{ minHeight: 365 }}>
      <ApexChart
        options={() => ({
          chart: { height: 350, type: "heatmap" },
          dataLabels: { enabled: false },
          colors: ["#008FFB"],
          series,
          xaxis: { type: "category" },
        })}
      />
    </div>
  );
}
```

```tsx title="src/app/(dashboard)/metrics/page.tsx"
import { MetricsHeatmap } from "@/components/metrics-heatmap";
import { pageMetadata } from "@/lib/site";

export const metadata = pageMetadata("/metrics");

export default function MetricsPage() {
  return (
    <div className="card">
      <div className="card-body">
        <MetricsHeatmap />
      </div>
    </div>
  );
}
```

`pageMetadata()` reads the route's title and description from `src/data/pages.json`, so add `/metrics` there.

The random values are generated at module level. The wrapper calls `options` again when the color mode changes, so values generated inside it would reshuffle the cells.

The server and the browser each run `Math.random()` and get different numbers. Only the browser draws the chart, though, so this causes no hydration mismatch: the browser's first render still matches the server HTML.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `options` | `(theme: Theme) => ApexOptions` | | The ApexCharts options, with `series` and `chart.type`. Called again with the new colors when the color mode changes |

## Colors

ApexCharts shades each row in one color, from light (low) to full (high). `colors: ["#008FFB"]` is the fixed blue of the HTML version. `colors: [theme.primary]` follows the theme instead.

In dark mode, the wrapper switches the tooltips only. It leaves ApexCharts' `theme.mode` alone, as that shades heatmaps towards black.

Sometimes a few named bands say more than a gradient. To map value bands to theme colors, use `plotOptions.heatmap.colorScale.ranges`. The legend shows each band's name:

```tsx
options={(theme) => ({
  chart: { height: 350, type: "heatmap" },
  dataLabels: { enabled: false },
  series,
  plotOptions: {
    heatmap: {
      colorScale: {
        ranges: [
          { from: 0, to: 30, color: theme.success, name: "Low" },
          { from: 31, to: 60, color: theme.warning, name: "Medium" },
          { from: 61, to: 90, color: theme.danger, name: "High" },
        ],
      },
    },
  },
})}
```

## Accessibility

- Shades are hard to read as numbers. `dataLabels: { enabled: true }` prints each value in its cell, if the cells are large enough at your width.
- Color ranges get a legend with their names, which gives the colors a key.
- Cell values show in the tooltip on hover only.
- Name the chart with `role="figure"` and `aria-label` on `ApexChart`: other props go to its `<div>`.

## Related

- [React heatmap chart](/react/docs/charts/heatmap-chart/): the same chart in AdminKit for React (Vite)
- [Bootstrap 5 heatmap chart](/bootstrap/docs/charts/heatmap-chart/): the ApexCharts example of the HTML version
- [Chart setup](/nextjs/docs/charts/overview/): the `ApexChart`, `ChartJs` and `VectorMap` client components
- [Next.js treemap chart](/nextjs/docs/charts/treemap-chart/): values as the size of rectangles
- [ApexCharts demo](https://nextjs-demo.adminkit.io/charts-apexcharts)

---

Canonical HTML page: https://adminkit.io/nextjs/docs/charts/heatmap-chart/

Site overview for agents: https://adminkit.io/llms.txt (expanded: https://adminkit.io/llms-full.txt) · Documentation: https://adminkit.io/bootstrap/docs/ · Questions: support@adminkit.io
