Add Heat Maps into Our React App with the Visx Library
Tags: react, javascript, webdev, beginners
Published: 2021-01-18 21:40:23

**Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62**

**Subscribe to my email list now at http://jauyeung.net/subscribe/**


Visx is a library that lets us add graphics to our React app easily.

In this article, we’ll look at how to use it to add heat maps into our React app.

### Install Required Packages

We have to install a few modules.

To get started, we run:

```
npm i @visx/group @visx/heatmap @visx/mock-data @visx/responsive @visx/scale
```

to install the packages.

### Add the Heatmaps

We can add the heatmaps by writing:

```
import React from "react";
import { Group } from "@visx/group";
import genBins from "@visx/mock-data/lib/generators/genBins";
import { scaleLinear } from "@visx/scale";
import { HeatmapCircle, HeatmapRect } from "@visx/heatmap";

const hot1 = "#77312f";
const hot2 = "#f33d15";
const cool1 = "#122549";
const cool2 = "#b4fbde";
const background = "#28272c";

const binData = genBins(/* length = */ 16, /* height = */ 16);

function max(data, value) {
  return Math.max(...data.map(value));
}

function min(data, value) {
  return Math.min(...data.map(value));
}
const bins = (d) => d.bins;
const count = (d) => d.count;

const colorMax = max(binData, (d) => max(bins(d), count));
const bucketSizeMax = max(binData, (d) => bins(d).length);

// scales
const xScale = scaleLinear({
  domain: [0, binData.length]
});
const yScale = scaleLinear({
  domain: [0, bucketSizeMax]
});
const circleColorScale = scaleLinear({
  range: [hot1, hot2],
  domain: [0, colorMax]
});
const rectColorScale = scaleLinear({
  range: [cool1, cool2],
  domain: [0, colorMax]
});
const opacityScale = scaleLinear({
  range: [0.1, 1],
  domain: [0, colorMax]
});
const defaultMargin = { top: 10, left: 20, right: 20, bottom: 110 };

const Example = ({
  width,
  height,
  events = false,
  margin = defaultMargin,
  separation = 20
}) => {
  const size =
    width > margin.left + margin.right
      ? width - margin.left - margin.right - separation
      : width;
  const xMax = size / 2;
  const yMax = height - margin.bottom - margin.top;
  const binWidth = xMax / binData.length;
  const binHeight = yMax / bucketSizeMax;
  const radius = min([binWidth, binHeight], (d) => d) / 2;

xScale.range([0, xMax]);
  yScale.range([yMax, 0]);

return width < 10 ? null : (
    <svg width={width} height={height}>
      <rect
        x={0}
        y={0}
        width={width}
        height={height}
        rx={14}
        fill={background}
      />
      <Group top={margin.top} left={margin.left}>
        <HeatmapCircle
          data={binData}
          xScale={(d) => xScale(d) ?? 0}
          yScale={(d) => yScale(d) ?? 0}
          colorScale={circleColorScale}
          opacityScale={opacityScale}
          radius={radius}
          gap={2}
        >
          {(heatmap) =>
            heatmap.map((heatmapBins) =>
              heatmapBins.map((bin) => (
                <circle
                  key={`heatmap-circle-${bin.row}-${bin.column}`}
                  className="visx-heatmap-circle"
                  cx={bin.cx}
                  cy={bin.cy}
                  r={bin.r}
                  fill={bin.color}
                  fillOpacity={bin.opacity}
                  onClick={() => {
                    if (!events) return;
                    const { row, column } = bin;
                    alert(JSON.stringify({ row, column, bin: bin.bin }));
                  }}
                />
              ))
            )
          }
        </HeatmapCircle>
      </Group>
      <Group top={margin.top} left={xMax + margin.left + separation}>
        <HeatmapRect
          data={binData}
          xScale={(d) => xScale(d) ?? 0}
          yScale={(d) => yScale(d) ?? 0}
          colorScale={rectColorScale}
          opacityScale={opacityScale}
          binWidth={binWidth}
          binHeight={binWidth}
          gap={2}
        >
          {(heatmap) =>
            heatmap.map((heatmapBins) =>
              heatmapBins.map((bin) => (
                <rect
                  key={`heatmap-rect-${bin.row}-${bin.column}`}
                  className="visx-heatmap-rect"
                  width={bin.width}
                  height={bin.height}
                  x={bin.x}
                  y={bin.y}
                  fill={bin.color}
                  fillOpacity={bin.opacity}
                  onClick={() => {
                    if (!events) return;
                    const { row, column } = bin;
                    alert(JSON.stringify({ row, column, bin: bin.bin }));
                  }}
                />
              ))
            )
          }
        </HeatmapRect>
      </Group>
    </svg>
  );
};

export default function App() {
  return (
    <div className="App">
      <Example width={500} height={300} />
    </div>
  );
}
```

We define the color codes for the heatmap at the top of the code.

`binData` has the data for the heatmap.

`max` and `min` returns the max and min values from the data.

`bins` and `count` are the getter methods for the data.

We get the max values for the color and bucket size with the `colorMax` and `bucketSizeMax` functions.

Next, we computed the `xScale` and `yScale` for the heatmaps to get the range of the values for the heatmap.

`circleColorScale` and `rectColorScale` are the color scales for the heatmap’s circles or rectangles.

`opacityStyle` have the opacity scale for the circles or rectangles.

In the `Example` component we get the width of the heatmap.

`binWidth` and `binHeight` have the width and height of the bins in the heatmap.

`radius` have the radius for the circles in the heatmap.

We pass all the variables into the `HeatmapCircle` component to create a heatmap with circles.

And the `HeatmapRect` component does the same to create a heatmap with rectangles.

The tiles are rendered in the render prop of each. We render `circle` or `rect` depending on the heatmap type.

### Conclusion

We can create a heatmap with different kinds of tiles in a React app with the Visx library.