Documents
heat-graph
heat-graph
Type
External
Status
Published
Created
Mar 17, 2026
Updated
Mar 17, 2026

import { HeatGraphDemo } from "@/app/heat-graph/heat-graph-demo";

heat-graph provides headless, Radix-style primitives for building GitHub-style activity heatmap graphs.

  • Composable — Radix-style compound components you fully control
  • Headless — Zero styling opinions, bring your own CSS/Tailwind
  • Tooltip built-in — Powered by Radix Popper for positioning
  • Customizable bucketing — Plug in your own classification function

Installation#

<Tabs items={["shadcn", "npm", "pnpm", "yarn"]}>

sh npx shadcn@latest add https://r.assistant-ui.com/heat-graph
This installs a pre-styled HeatGraph component to components/assistant-ui/heat-graph.tsx along with the heat-graph package.


sh npm install heat-graph


sh pnpm add heat-graph


sh yarn add heat-graph

Quick Start#

"use client";

import * as HeatGraph from "heat-graph";

const COLORS = ["#ebedf0", "#c6d7f9", "#8fb0f3", "#5888e8", "#2563eb"];

export function ActivityGraph({ data }: { data: HeatGraph.DataPoint[] }) {
  return (
    <HeatGraph.Root data={data} weekStart="monday" colorScale={COLORS}>
      <HeatGraph.Grid className="gap-[3px]">
        {() => (
          <HeatGraph.Cell className="aspect-square rounded-sm" />
        )}
      </HeatGraph.Grid>
      <HeatGraph.Tooltip>
        {({ cell }) => (
          <div>
            {cell.count} contributions on {cell.date.toLocaleDateString()}
          </div>
        )}
      </HeatGraph.Tooltip>
    </HeatGraph.Root>
  );
}
Components using Heat Graph must be Client Components (`"use client"`), since they rely on React Context and interactivity.

Anatomy#

import * as HeatGraph from "heat-graph";

<HeatGraph.Root data={data} colorScale={colors}>

  {/* Month labels — iterates internally, renders each label */}
  <HeatGraph.MonthLabels>
    {({ label, totalWeeks }) => (
      <span style={{ left: `${(label.column / totalWeeks) * 100}%` }}>
        {HeatGraph.MONTH_SHORT[label.month]}
      </span>
    )}
  </HeatGraph.MonthLabels>

  {/* Day-of-week labels — iterates internally, renders each label */}
  <HeatGraph.DayLabels>
    {({ label }) => (
      <span>{HeatGraph.DAY_SHORT[label.dayOfWeek]}</span>
    )}
  </HeatGraph.DayLabels>

  {/* Grid + Cells — iterates internally, renders each cell */}
  <HeatGraph.Grid>
    {() => <HeatGraph.Cell />}
  </HeatGraph.Grid>

  {/* Legend — iterates internally, renders each level */}
  <HeatGraph.Legend>
    {() => <HeatGraph.LegendLevel />}
  </HeatGraph.Legend>

  {/* Tooltip */}
  <HeatGraph.Tooltip>
    {({ cell }) => <div>{cell.count} on {cell.date.toLocaleDateString()}</div>}
  </HeatGraph.Tooltip>

</HeatGraph.Root>

API Reference#

Root#

The top-level provider. Renders a <div> that computes the grid layout and provides state to all children. Accepts all standard div props.

PropTypeDefaultDescription
dataDataPoint[]requiredArray of { date: string | Date, count: number }
startstring | Date1 year before endStart of the date range
endstring | DatetodayEnd of the date range
weekStart"sunday" | "monday""sunday"First day of the week
classifyClassifyFnautoLevels(5)Bucketing function mapping counts to levels
colorScalestring[]Array of colors, one per level (index 0 = lowest)

Grid#

A <div> with CSS Grid layout. Renders gridTemplateColumns and gridTemplateRows based on the computed data. Accepts all standard div props.

Iterates over cells internally, calling the children render function for each cell. Each cell is wrapped in a context that Cell reads from.

type CellData = {
  date: Date;
  count: number;
  level: number;
  column: number;
  row: number;
};

Cell#

A <div> that reads from cell context. Automatically applies:

  • Grid positioning (gridColumn, gridRow)
  • Background color from colorScale
  • Tooltip hover handlers

Accepts all standard div props. Pass colorScale to override the Root-level color scale.

MonthLabels#

Iterates over month labels, calling the children render function for each label.

<HeatGraph.MonthLabels>
  {({ label, totalWeeks }) => (
    <span style={{ left: `${(label.column / totalWeeks) * 100}%` }}>
      {HeatGraph.MONTH_SHORT[label.month]}
    </span>
  )}
</HeatGraph.MonthLabels>

Each label has { month: number, column: number }. Use totalWeeks to compute label positions. Use MONTH_SHORT[label.month] for English labels, or format with Intl.DateTimeFormat for localization.

DayLabels#

Iterates over day-of-week labels, calling the children render function for each label.

<HeatGraph.DayLabels>
  {({ label }) => (
    <span>{HeatGraph.DAY_SHORT[label.dayOfWeek]}</span>
  )}
</HeatGraph.DayLabels>

Each label has { dayOfWeek: number, row: number } where dayOfWeek is 0=Sun..6=Sat. Use DAY_SHORT[label.dayOfWeek] for English labels, or format with Intl.DateTimeFormat for localization.

Legend#

Iterates over legend levels, calling the children render function for each item. Each item has { level: number, color: string | undefined }.

LegendLevel#

A <div> that reads from legend item context. Automatically applies backgroundColor from the color scale. Use inside Legend.

Tooltip#

Renders only when a cell is hovered. Positioned by Radix Popper relative to the hovered cell. Accepts Radix Popper Content props (side, sideOffset, align, etc.).

<HeatGraph.Tooltip side="top" sideOffset={8} className="...">
  {({ cell }) => <div>{cell.count} contributions</div>}
</HeatGraph.Tooltip>

autoLevels(n)#

Default classification function. Maps counts into n evenly-distributed levels (0 to n-1). Level 0 is always count 0.

type ClassifyFn = (counts: number[]) => (count: number) => number;

To provide a custom classifier:

const myClassify: HeatGraph.ClassifyFn = (counts) => {
  const p75 = percentile(counts, 75);
  return (count) => {
    if (count === 0) return 0;
    if (count < p75 * 0.25) return 1;
    if (count < p75 * 0.5) return 2;
    if (count < p75) return 3;
    return 4;
  };
};

<HeatGraph.Root data={data} classify={myClassify}>

MONTH_SHORT#

English month abbreviations array: ["Jan", "Feb", ..., "Dec"]. Index by MonthLabel.month.

DAY_SHORT#

English day abbreviations array: ["Sun", "Mon", ..., "Sat"]. Index by DayLabel.dayOfWeek.