Trend · Intermediate

Calendar heatmap

Daily amounts on a weekly grid.

Open in playground
Use when
The rhythm of daily activity, such as commits or orders
Avoid when
The period is shorter than a month

Try it

28

Calendar heatmap

Daily commits in 2025
Calendar heatmapCalendar heatmap. 365 days, max 16.
Calendar heatmap data
Wed, January 14
Thu, January 24
Fri, January 35
Sat, January 41
Sun, January 50
Mon, January 61
Tue, January 71
Wed, January 81
Thu, January 93
Fri, January 103
Sat, January 110
Sun, January 120
Mon, January 132
Tue, January 1411
Wed, January 154
Thu, January 161
Fri, January 172
Sat, January 181
Sun, January 190
Mon, January 202
Tue, January 214
Wed, January 223
Thu, January 231
Fri, January 242
Sat, January 250
Sun, January 262
Mon, January 274
Tue, January 283
Wed, January 291
Thu, January 303
Fri, January 310
Sat, February 110
Sun, February 21
Mon, February 33
Tue, February 43
Wed, February 53
Thu, February 62
Fri, February 710
Sat, February 81
Sun, February 93
Mon, February 102
Tue, February 115
Wed, February 127
Thu, February 134
Fri, February 144
Sat, February 152
Sun, February 160
Mon, February 172
Tue, February 182
Wed, February 192
Thu, February 203
Fri, February 215
Sat, February 221
Sun, February 232
Mon, February 243
Tue, February 255
Wed, February 264
Thu, February 274
Fri, February 283
Sat, March 11
and 305 more
Weekends are quietIntermediate

Hover over the chart, or Tab in and use the arrow keys to move between items. The numbers are sample data.

Variants

Named looks for this chart. Each one is the example above with a few options or fields changed. The CLI (ztd-charts example calendar --variant <id>) and the MCP server know the same names.

Weeks from Monday

Daily commits in 2025
Sample data

No gaps

Daily commits in 2025
Sample data

Code

CalendarHeatmap.tsx
import { ChartCard, CalendarHeatmap } from "ztd-charts";

const data = [
  {"date":"2025-01-01","commits":4},
  {"date":"2025-01-02","commits":4},
  {"date":"2025-01-03","commits":5},
  {"date":"2025-01-04","commits":1},
  {"date":"2025-01-05","commits":0},
  {"date":"2025-01-06","commits":1},
  // … 359 more rows
];

export function Example() {
  return (
    <ChartCard number="28" title="Calendar heatmap" subtitle="Daily commits in 2025" takeaway="Weekends are quiet" level="intermediate" replay>
      <CalendarHeatmap
        data={data}
        date="date"
        value="commits"
      />
    </ChartCard>
  );
}

Props

<CalendarHeatmap> takes these props. Rows marked Common work the same on every chart.

31 props

Prop
dataRequiredAn array of objects. Pass CSV, REST or database rows as they areobject[]
dateRequiredDate fieldstring | (d, i) => date
valueRequiredValue fieldstring | (d, i) => number
weekStartFirst day of the week (0 Sunday, 1 Monday)numberDefault 0
gapGap between cells (px)numberDefault 2.5
canvasThresholdDay count that switches to canvasnumberDefault 4000
dayFirstReads dates such as "05/03/2024", where either part could be the month, day first (5 March). Without it, a clear date in the column decides (25/03/2024), otherwise month first. true also reads "05.03.24" as day.month.yearboolean
colorReverseDarkest color for the smallest value, for rankings where 1st should stand out. The default highlight moves to the smallest value toobooleanDefault false
colorCenterValue that gets the lightest color. Values above take the accent color and values below take gray, so gains and losses split there (for example 0)number
colorStepsColor in this many steps instead of a smooth scale. The heatmap legend shows each stepnumber
heightCommonHeight in px. Without it, each chart picks a default that fits its widthnumber
widthCommonFixed width in px. Without it, the chart fits its parentnumber
highlightCommonItem to highlight. An index, a name, an array, or "max" | "min" | "first" | "last" | "none"number | string | (number|string)[] | "max" …
formatCommonValue format. "number" | "integer" | "percent" (values are already percents: 42 → "42%") | "ratio" (0.42 → "42%") | "compact" | "currency" (KRW for Korean locales, otherwise USD; for another currency pass Intl options with currency) | "decimal" or Intl.NumberFormat options"number" | "percent" | "compact" | …
unitCommonUnit appended to values. For example "%" or "ms"string
localeCommonLocale for numbers, dates and the text a chart writes itself (tooltips, screen reader sentences). "ko" or "ko-*" switches to Korean. Falls back to the ThemeProvider localestringDefault "en-US"
animateCommonEntry and update animationbooleanDefault true
durationCommonAnimation length (ms)number
easingCommonMotion curve"spring" | "easeOut" | "easeInOut" | "linear"Default "spring"
staggerCommonDelay between items as they enter (ms)number
reducedMotionCommon"auto" follows the OS setting. true always turns motion offanyDefault "auto"
animateOnViewCommonPlay when the chart scrolls into viewbooleanDefault true
showLabelsCommonLabels drawn on the chart (values and names)booleanDefault true
tooltipCommonShow tooltips. { template: "{label} · {value} · owner {owner}" } writes them: {label}, {value}, {series} or any other column of the row. \n starts a new line, and a line with an empty value is left outtooltipDefault true
syncIdCommonCharts with the same syncId show the hovered x together (line, area, bar, candlestick). <ChartGrid sync> sets one for every chart inside itstring
ariaLabelCommonName for screen readersstring
descriptionCommonDescription for screen readers. Built from the data when omittedstring
onHoverCommonRuns when hover or keyboard focus moves to another item(e: ChartEvent | null) => void
onClickCommonRuns on click, Enter or Space(e: ChartEvent) => void
replayKeyCommonReplays the entry animation when it changesstring | number
themeCommonChanges theme tokens for this chart onlyPartial<ChartTheme>

Above 4,000 items it draws on a canvas. Axes and labels stay SVG.