A contribution calendar in the GitHub style: one square per day in week columns, shaded from a single color in five steps whose thresholds follow the data's quantiles or your own. Month and weekday labels, a Less to More legend and an optional total frame the grid; a rolling range of weeks or a whole calendar year, weeks starting on Sunday or Monday, and names and numbers in any locale. Hovering or focusing a day lifts it with a tooltip of its date and value; the grid is one tab stop with arrow keys by day and week, and days can be made selectable. All date maths is in UTC, so a day never shifts with the reader's time zone, and wide ranges scroll sideways, opening on the latest week.
The import and the props worth knowing about, in one place.
import { HeatmapCalendar } from "@/components/beste/component/heatmap-calendar";
// A rolling year that ends on the latest date in the data
<HeatmapCalendar data={[{ date: "2026-09-27", value: 4 }, { date: "2026-09-26", value: 1 }]} />
<HeatmapCalendar
data={activity}
year={2026} // a whole calendar year instead of a rolling range
weekStartsOn={1} // Monday
locale="de-DE"
color="#16a34a" // any CSS color; lighter steps are mixed from it
levels={5} // shades including the empty one
thresholds={[1, 3, 6, 10]} // lowest value of each non-empty level
unit={["session", "sessions"]}
showTotal
onSelectedChange={(date, day) => console.log(date, day.value)}
/>Every one of these is in the component already. They are listed because a props table cannot mention a gesture, so nothing else on this page can tell you they exist.
| Up / Down | The day before or after. |
|---|---|
| Left / Right | The same weekday a week earlier or later. |
| Page Up / Page Down | Four weeks back or forward. |
| Home / End | The first or last day of the week; with Cmd or Ctrl, of the whole range. |
Read from the component's own type, so this cannot drift from what it accepts.
| Prop | Type | Default | Description |
|---|---|---|---|
data | HeatmapDay[] | — | |
endDate | string | — | Last day shown. Defaults to the latest date in `data`, or today once mounted. |
weeks | number | — | Weeks shown, counting back from `endDate`. Ignored when `year` is set. |
year | number | — | Show one calendar year, January to December, instead of a rolling range. |
weekStartsOn | 0 | 1 | — | 0 starts weeks on Sunday, 1 on Monday. |
locale | string | — | Locale for month and weekday names and for numbers. |
color | string | — | The scale's strongest color, any CSS color. Lighter levels are mixed from it. |
levels | number | — | Number of shades including the empty one, 3 to 9. |
thresholds | number[] | — | Lowest value of each non-empty level, ascending. Computed from quantiles when left out. |
unit | [string, string] | — | Singular and plural word for the value, used in labels and the total. |
formatValue | (value: number, date: string) => string | — | Custom text for a day's value in the tooltip and its label. |
label | string | — | Accessible name of the grid. |
showMonthLabels | boolean | — | |
showWeekdayLabels | boolean | — | |
showLegend | boolean | — | |
showTotal | boolean | — | Total of the range, shown beside the legend. |
selected | string | null | — | Selected day, controlled. |
defaultSelected | string | null | — | |
onSelectedChange | (date: string, day: HeatmapDay) => void | — | Fires when a day is picked with a click, Enter or Space. Days become selectable when this or `selected` is set. |
size | "sm" | "default" | "lg" | — | |
tone | "muted" | "outline" | "ghost" | — | |
className | string | — |