Components / Charts

ActivityHeatmap

See a year of activity at a glance, one square per day.

Install

npx shadcn@latest add https://irisui.dev/r/activity-heatmap.json
import { ActivityHeatmap } from "@/components/iris/activity-heatmap/activity-heatmap";
Registry item activity-heatmapBrings in foundationRegistry JSON

Preview

ActivityHeatmap

A contribution-style calendar with one square per day, weeks as columns and four accent tints for how busy each day was.

When to use

  • A year (or a few months) of daily counts where rhythm, streaks and quiet stretches matter more than exact comparisons: deploys, commits, sessions, messages.
  • With a range switch: put it in actions and swap the days data; the grid recolours in a sweep.
  • Not when the exact comparison between days is the point; use BarChart.

Usage

import { ActivityHeatmap } from "@/components/iris/activity-heatmap/activity-heatmap";

function DocumentChanges({ days }) {
  const [selected, setSelected] = React.useState(null);
  return h(ActivityHeatmap, {
    days, // [{ date: '2026-10-21', count: 7 }, ...] oldest first
    label: "Document changes in notdocs, last 12 months",
    period: "the last year",
    unit: { one: "change", other: "changes" },
    weekStartsOn: 1,
    selectedDate: selected,
    onSelectDate: setSelected,
  });
}

Props

Prop Type Default Description
days { date: string; count: number }[] required One entry per day as YYYY-MM-DD, oldest first. Missing days inside the range count as zero.
label string required Accessible name for the grid.
period string required Finishes the summary line: "1,038 changes in {period}".
unit { one: string; other: string } contribution / contributions Nouns for the count.
thresholds [number, number, number] quarters of the busiest day Upper bounds for levels one to three; anything above is level four.
weekStartsOn 0 | 1 0 Sunday or Monday as the first row.
selectedDate string | null — Highlighted day.
onSelectDate (date: string) => void — Called on click, Enter or Space.
actions ReactNode — Controls beside the summary, such as a range switch.
locale string "en-US" Formatting locale; fixed by default so server and client match.
className string — Class on the root.

Notes

  • Keyboard: arrow keys move by day and week, Home/End go to the range edges, Enter/Space selects, Escape dismisses the tooltip.
  • Accessibility: cells are labelled, a hidden legend explains the levels, legend swatches toggle to highlight one level, and totals and the hovered day are announced. The tooltip itself is hidden from assistive technology.
  • Motion: cells wave in once on view; the tooltip glides between cells; a new range recolours in a sweep. Reduced motion skips these.
  • Tints are mixed from --accent over --ink/--surface, so it follows the theme. A full year needs roughly 700px; in narrower containers the grid scrolls sideways (the tooltip stays pinned to its cell) and the summary switches to a short caption below 440px.