# How to Make a Heatmap in Figma (and Why the Row Order Is the Design)
_August 26, 2026 · 14 min read · by Systergon_
> A heatmap trades the most accurate encoding for the most scalable one: colour is the worst channel human vision reads, and that is the price of showing four hundred cells at once. Which makes it worth drawing only when there is a pattern to see — and whether there is one depends almost entirely on how you order the rows. What it is for, the three times it is the wrong tool, the grid editor ECharts gets and the table the others use, why one library here cannot draw a matrix heatmap at all, and how to pick a colour scale that is not lying.
Tags: Figma, Charts, Heatmap, Data Visualization, Plugin
URL: https://systergon.com/blog/figma-charts-heatmap
---
[Install Figma Charts — it’s free to try →](https://www.figma.com/community/plugin/1345221583582136577/figma-charts) [See the heatmap gallery →](https://systergon.com/figma-charts/chart-types/heatmap)

A heatmap makes a deliberate trade that no other chart makes so openly. Colour is the least accurate thing human vision judges — worse than length, worse than position, worse than angle — and a heatmap encodes every one of its values in colour anyway. What it buys with that sacrifice is scale: a grid of four hundred cells is legible at a glance in a way that four hundred bars never could be, because you are no longer reading values, you are reading a _pattern_.

Which means a heatmap is only worth drawing when there is a pattern to see, and whether there is one depends almost entirely on a decision most people make by accident: the order of the rows and columns. This is what a heatmap is for, when it is the wrong tool, how to build one in Figma against your real numbers, what each library gives you — including the one that cannot draw a matrix heatmap at all — and how to choose a colour scale that is not lying to your reader.

![The Figma Charts gallery showing live chart previews in a four-column grid, each rendered with the current colour palette](https://systergon.com/figma-charts-help/chart-grid-light.webp)

## First — get the plugin

Everything below happens inside **Figma Charts**, a Figma plugin. It does not draw charts of its own: it runs the real JavaScript charting libraries — Google Charts, Highcharts, D3-based Nivo, ApexCharts and Apache ECharts — inside the plugin window, so the heatmap on your canvas is the library’s own output, coloured by its own scale.

[Install Figma Charts from the Figma Community →](https://www.figma.com/community/plugin/1345221583582136577/figma-charts)

Press **⌘/** (**Ctrl /** on Windows), type _Figma Charts_, hit Enter, and pick **Heatmap** from the category filter. There are **18 heatmap examples** waiting, and they are not evenly spread — see [library by library](#libraries).

## What a heatmap is, and the question it answers

A heatmap answers **“where are the hot spots?”** — across two dimensions at once, for far more cells than you could ever label.

Every cell sits at the intersection of a row and a column, and its colour carries the value. That is the entire grammar. Because position is doing the categorising and colour is doing the measuring, you can scale a heatmap to hundreds of cells without the chart falling apart: ECharts ships one in this plugin with **20,000 data points** in it, and it still reads.

What you give up is precision. A reader can tell you a cell is _darker_ than its neighbour; they cannot tell you it is 18% larger, and they will not reliably rank two cells from opposite corners of the grid. If your reader needs to compare individual values, you want a [bar chart](https://systergon.com/blog/figma-charts-bar-chart) or a table — and if they need to compare a few values inside a big grid, the right answer is often a heatmap with the numbers printed in the cells, which is a table that has been made scannable.

Four members of the family, all of which the plugin files under **Heatmap**:

- **The matrix heatmap** — rows, columns, values. The default meaning of the word.
- **The calendar heatmap** — the grid is days, laid out as weeks against weekdays. The GitHub contribution graph.
- **The correlation matrix** — a square, symmetric heatmap of every variable against every other, and the one case where the colour scale has a fixed, meaningful range.
- **The tilemap** — the same idea with the cells drawn as hexagons or circles instead of squares, which Highcharts alone offers here.

## When to use it — and three times you should not

Use a heatmap when you have two categorical dimensions and one measure, when the grid is large enough that reading every value is not the goal, and when the thing you want the reader to notice is a _region_ — a hot row, a cold block, a diagonal.

Three cases where it is the wrong tool, in order of how often the mistake is made:

**1. You have a handful of values.** A 3×4 heatmap is twelve numbers rendered in the least precise encoding available, and it will be read more slowly and less accurately than the same twelve numbers in a table. The heatmap earns its imprecision at scale; below about thirty cells it is paying the cost without collecting the benefit.

**2. The comparison that matters is within one row.** If your reader’s real question is “which region sold most in March?”, they are comparing values along a single line, and grouped bars do that better than any colour scale can. Heatmaps are for questions that span the whole grid: which regions and which months, together, are unusual.

**3. Your grid is mostly empty.** Sparse matrices make bad heatmaps, because the eye reads the blank cells as a pattern of their own — and if you fill the gaps with the scale’s lowest colour instead, you have silently claimed that missing data is a measured zero. When most intersections have no observation, a [scatter plot](https://systergon.com/blog/figma-charts-scatter-plot) or a ranked bar chart of the pairs that do exist tells the truth more cheaply.

A fourth, which is really a warning: **when one value is enormously larger than the rest.** A single outlier stretches the colour scale until every other cell is the same pale shade, and the chart quietly becomes a picture of one number. That has fixes — capping, banding, a log scale, annotating the outlier — and they are in [reading it well](#reading).

## The data shape it needs

A heatmap is the plugin’s one **matrix** shape: two sets of categories and a value at each intersection. There are two honest ways to write that down, and the plugin uses both.

**In ECharts, a heatmap opens a dedicated grid editor** rather than the ordinary data table. It has three sections — **X-Axis** and **Y-Axis** for the two sets of category names, and **Values** for the cells themselves — and each section can be connected to its own data source independently, so your column headers can come from one place and your numbers from another. Two things it does for you are worth knowing: it writes the cell values back as the `[x, y, value]` triplets ECharts expects, and it **rescales the colour legend’s maximum to your new data**, so the scale stops being anchored to whatever the sample chart’s biggest number was.

![The matrix data editor in Figma Charts: connect buttons for the X-axis, Y-axis and Values sources above a grid of editable cells tinted by value, with a row-by-column count and value range along the bottom](https://systergon.com/figma-charts-help/matrix-editor-light.webp)

Three smaller things in that editor earn their place. The cells are **tinted by value as you type**, so the pattern appears in the editor before you look at the chart. The footer reports the grid as _rows × columns = cells_ alongside the current value **range**, which is the number your colour scale is about to be built on. And the corner button **transposes** the whole grid — swapping rows for columns — which is the one-click fix for a heatmap whose column labels are too long to read.

**In ApexCharts and Nivo the same matrix arrives as an ordinary table** — one row per heatmap row, one column per heatmap column, which is exactly how you would type it into a spreadsheet. That is the shape most people already have, and pasting a block of cells from Sheets or Excel lands correctly.

Two cautions, both worth more than they look:

- **Blank is not zero.** In a column the plugin has judged numeric, a cell that will not parse becomes **0** rather than a gap — and in a heatmap a zero is a real colour at the bottom of the scale, indistinguishable from a genuinely measured minimum. Clear the cells you mean to be empty, and decide deliberately whether an empty intersection should be blank or coloured.
- **Get the numbers in before you tune the scale.** Every colour decision is a function of your actual range, and the sample data’s range is not yours.

## Build it in Figma Charts

The fastest honest route is five steps:

1. **Open the plugin and filter to Heatmap.** Every tile is a live chart rather than a picture, so what you see is what you will get.
2. **Pick a starting point that matches your _structure_** — a plain matrix, a calendar, a correlation matrix, a hexagonal tilemap. The colour scheme is the easiest thing to change later; the structure is not.
3. **Put your real values in.** In ECharts that is the grid editor above; in ApexCharts and Nivo it is the table, one row per row.
4. **Order the rows and columns.** Not alphabetically. This is the step that decides whether the chart has a pattern in it, and it is covered in full [below](#reading).
5. **Insert with + SVG.** The chart arrives as real vector and text layers — every cell a rectangle you can select, restyle and prototype with.

Two shortcuts worth knowing. The **Essential / Common / Advanced** control in the editor header sets how much furniture the chart carries — and for a heatmap, Essential drops the colour legend, which is the one piece of furniture a heatmap genuinely cannot do without. And if you want to change one element rather than hunt the option tree, turn on the **Inspector** and click the thing; it works on Highcharts and ApexCharts.

## Library by library

This is the chart type with the most lopsided coverage in the plugin, and one genuine gap. The **18 examples** break down like this:

- **ECharts — 11 examples**, and the only library with a purpose-built grid editor for the data. It carries the range as well: a plain _Heatmap on Cartesian_, a _Discrete Mapping of Color_ that bands the scale into steps instead of a continuous ramp, a _20K data_ heatmap for the case where the grid is enormous, a correlation matrix, a covariance matrix, and four calendar heatmaps. If your heatmap is the centre of the design, start here.
- **Highcharts — 4 examples**, and the widest spread of _forms_: a standard heatmap, a **hexagonal tilemap** (the honeycomb layout, which no other library here offers), a calendar heatmap, and a correlation heatmap built on a genuinely diverging colour axis — red at −1, white at 0, blue at +1. It needs a commercial licence to ship in a commercial product.
- **ApexCharts — 2 examples**, a basic heatmap and a colour-range one that maps bands of values to named colours. Clean defaults, the second library the Inspector works with, and the table editor round-trips its data cleanly — a good choice when the heatmap is one tile in a product dashboard.
- **Nivo — 1 example.** The D3 look, a sequential blue-green scheme by default, and the thinnest bench here. Remember also that **Nivo exports to React only**.
- **Google Charts — none.** Google Charts has no heatmap chart type, and the plugin does not invent one. What it does have is the **Calendar** chart — five examples of it, filed under the [Calendar](https://systergon.com/figma-charts/chart-types/calendar) category rather than this one — which is a calendar heatmap in everything but name. If you need a matrix heatmap and your team is on Google Charts, this is one of the few places in the plugin where the answer is genuinely another library.

One piece of taxonomy that will save you a search: **calendar heatmaps are split across two categories.** ECharts’ four and Highcharts’ one sit under _Heatmap_, because that is what they are made of; Google’s five and Nivo’s one sit under _Calendar_. Look in both.

The rule that beats all of the above: **if your engineers already use one of the five, design in that one.** The chart you approve is then the chart they ship, because the export lists that library’s own npm package at the same major version the plugin rendered with. [Choosing the right chart library](https://systergon.com/blog/figma-charts-choosing-the-right-chart-library) covers the trade-offs in full.

## Styling it well

Nearly every styling decision in a heatmap is a colour decision, and the colour scale is the chart’s axis. Design it with the seriousness you would give an axis.

**Sequential for magnitude, diverging for a meaningful middle.** If your values run from low to high — counts, durations, revenue — use a single-hue sequential ramp where lightness increases monotonically with value. If they run either side of a natural centre — correlation around zero, change against a target, profit and loss — use a diverging scale with a neutral midpoint and two hues, like the −1 to +1 red-white-blue in the Highcharts correlation example. Using a diverging scale for sequential data invents a boundary that does not exist in your numbers.

**Do not use a rainbow.** It is the most common heatmap failure and the hardest to unsee once you know: a rainbow ramp is not monotonic in lightness, so it manufactures edges wherever the hue changes fastest and flattens differences elsewhere. Readers see banding that is in the palette rather than the data. Perceptually uniform ramps — viridis and its relatives — exist precisely to fix this, and every library here can take an explicit list of stops if its default scheme is not one.

**Consider banding the scale on purpose.** A continuous ramp is honest about magnitude and hard to read precisely; discrete steps — five or six bands with labelled thresholds — are easier to read off and easier to remember, at the cost of inventing boundaries. ECharts ships a discrete-mapping example for exactly this. Choose deliberately: continuous when the pattern matters, banded when the categories of value matter (“under SLA / at SLA / over SLA”).

**Give the cells a hairline gap.** A one or two pixel gap between cells stops the grid reading as a continuous field and makes the individual cell the unit again. It also gives you somewhere for the row and column structure to breathe.

**Label the cells when the grid is small enough.** Under about fifty cells, printing the value inside each one costs nothing and turns an imprecise chart into a precise one that still shows the pattern. Above that, labels become noise — and that is the point at which colour is doing the work alone.

**Square cells, sensible labels.** Cells should be close to square unless one dimension genuinely deserves more room; long column labels want rotation or, better, a transpose — the grid editor has a button for it — so they become row labels with a whole line each. For type and colour, bind them to your design system rather than picking by hand — the plugin can build a palette from your Figma colour variables, which is covered in [Your chart, your design system](https://systergon.com/blog/figma-charts-design-system-colors-typography-tokens).

## Reading it well — and the ways it misleads

**The order of the rows and columns is the design.** This is the single most consequential thing on this page. A heatmap has no inherent order — unlike a line chart, whose x axis is fixed by time, you are free to arrange the rows however you like, and the arrangement determines whether a pattern is visible at all. Alphabetical order is the default in most tools and it is almost always the worst one: it scatters related rows across the grid and turns a block structure into confetti. Sort by a row total, by the value of a key column, or by similarity so that rows which behave alike sit together. Do that and clusters appear as blocks; leave it alphabetical and the same data looks like noise. Both pictures are “accurate”; only one is informative.

**One outlier can eat the entire scale.** Because the colour ramp stretches from your minimum to your maximum, a single value ten times larger than the rest compresses everything else into the bottom of the ramp, and the chart becomes a map of one cell. The fixes are all legitimate provided you disclose them: cap the scale at a percentile and mark the cells that exceed it, use a logarithmic mapping, or band the scale. What is not legitimate is quietly clipping the value.

**Two heatmaps side by side need the same scale.** Small multiples are one of the best uses of this chart type — the same grid for each quarter, or each cohort — and they only work if every panel shares one colour scale and one legend. Per-panel scales make every panel look equally intense, which is precisely the comparison the reader thinks they are making and precisely the one they are not.

**Colour reads as importance, and area amplifies it.** A large block of mid-tone cells will dominate a reader’s attention over a single dark one, regardless of the values. If the important finding is one cell, annotate it — a heatmap does not have a natural way to say “look here”, so add one.

**Dark should mean more.** It is a convention rather than a law, but it is a strong one, and inverting it — pale for high values on a white background — costs your reader a conscious translation on every cell. If your palette forces a light-for-high ramp, say so in the legend and expect it to be misread anyway.

**And a correlation matrix is symmetric.** Every value appears twice, mirrored across a diagonal of perfect self-correlation, which means half the chart is redundant and the diagonal is decoration. Showing only the lower triangle is not a trick; it is removing a duplicate.

## Accessibility

A heatmap encodes everything in colour, which makes it the hardest chart type in this series to make accessible — and the most rewarding to get right, because the fixes are concrete.

**Choose a colour-vision-safe ramp.** Around one in twelve men has some form of colour vision deficiency, and the classic red-to-green diverging scale is precisely the pairing that fails. Red-to-blue, as in the Highcharts correlation example, survives it. Single-hue sequential ramps are the safest of all, because they vary in lightness rather than hue — and lightness is the one channel everybody reads.

**Print the values where you can.** A labelled cell is readable without colour vision at all. On a small grid this removes the accessibility problem rather than mitigating it.

**Check contrast of the labels against the cells, at both ends of the scale.** Text that passes over a pale cell will fail over the dark end of the same ramp. Either switch the label colour by threshold or choose a ramp whose range keeps one text colour legible throughout.

**Give it a text equivalent.** The Export tab has three buttons most people never notice: **Copy Alt Text** generates a description, **Copy Data Table** produces an HTML table of the underlying numbers, and **Set Node Desc** writes the description onto the Figma node so it travels with the design. For a heatmap the data table is the genuinely correct answer — a matrix _is_ a table, so the accessible version loses nothing but the colour.

## Export and handoff

Because the chart was rendered by a real charting library, the handoff carries the chart rather than a picture of it. From the **Export** tab:

- **Code** writes a working component for React, Vue 3, Angular, Svelte or vanilla JavaScript, listing the library’s own npm packages at the same major version the plugin rendered with.
- **Config** gives you the library’s options object on its own — including the colour stops, which is the part of a heatmap most likely to be re-invented badly in code.
- **Tokens** exports the colours and typography as CSS, SCSS, Tailwind, W3C design tokens or JSON.

The whole of that, including the accessibility exports and the embed generator, is in [From Figma to code](https://systergon.com/blog/figma-charts-export-react-vue-svelte-angular-design-tokens).

## Common questions

### Can Google Charts make a heatmap?

Not a matrix heatmap — Google Charts has no heatmap chart type, and the plugin ships none. It does have the **Calendar** chart, five examples of it, which is a calendar heatmap of days. For a rows-and-columns heatmap, use ECharts, Highcharts, ApexCharts or Nivo.

### What colour scale should I use?

Sequential — one hue, lightness rising with value — whenever your values simply go from low to high. Diverging, with a neutral midpoint, only when there is a meaningful centre such as zero, a target or a baseline. Avoid rainbow ramps: they are not monotonic in lightness, so they invent edges the data does not have.

### How many rows and columns can a heatmap take?

Many more than a bar chart — hundreds of cells is normal and ECharts ships a 20,000-point example. The real limit is the labels: once your row and column names no longer fit legibly, the chart stops being readable even though the pattern is still there. Transpose, abbreviate, or group.

### Why does my heatmap open a grid editor instead of the data table?

Because in ECharts the plugin gives heatmaps a purpose-built matrix editor: X-axis categories, Y-axis categories and a grid of values, each section connectable to its own source. It writes your values back as the `[x, y, value]` triplets ECharts expects and rescales the colour legend to your data. In ApexCharts and Nivo the same matrix is edited as an ordinary table.

### Should I sort the rows?

Almost always, and rarely alphabetically. Row and column order is what makes a pattern visible — sort by totals, by a key column, or by similarity so that rows behaving alike sit together. Alphabetical order scatters related rows across the grid and hides the block structure that is the reason to draw a heatmap at all.

### What should an empty cell look like?

Not like a zero. Give missing intersections no fill (or a distinct neutral) so they read as absent rather than measured, and watch for the plugin’s numeric coercion — a cell that will not parse becomes 0, which lands at the bottom of your colour scale looking exactly like a real minimum.

### What is a calendar heatmap, and where is it?

A heatmap whose grid is the calendar: weeks across, weekdays down, one cell per day. It is split across two categories in the plugin — ECharts’ four and Highcharts’ one are under Heatmap, Google’s five and Nivo’s one are under [Calendar](https://systergon.com/figma-charts/chart-types/calendar). Look in both before concluding a library cannot do it.

### Does the chart stay editable in Figma?

Insert as **SVG** and every cell is a real rectangle and every label real text, which makes a heatmap one of the most rewarding types to insert as vector — you can restyle individual cells to annotate a finding. Insert as **PNG** and you get a flat image at twice the pixel density. Either way the chart reopens in the editor with its settings intact.

## Where to start

Open the plugin, filter to Heatmap, put your real values into an ECharts example — and then, before touching a single colour, sort the rows by their totals and look again. If a pattern appears, you have the right chart. If nothing appears after a couple of orderings, the honest conclusion is that there is no two-dimensional pattern to show, and that is worth knowing before you spend an afternoon on the palette.

[Install Figma Charts — it’s free to try →](https://www.figma.com/community/plugin/1345221583582136577/figma-charts) [Browse all 45 chart types →](https://systergon.com/figma-charts/chart-types)