Activity heatmap

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

datachartcalendar
Preview
Activity heatmap preview

commits in 1,487 commits in May to August 2026

Darker squares mean more commits. Levels: no commits, 1 to 7 commits, 8 to 13 commits, 14 to 20 commits, 21 or more commits.

When to use

  • Daily rhythm over a long range, like contributions or workouts.
  • Views where streaks and quiet weeks matter more than exact comparison.

When not to use

  • Use bar-chart when the exact comparison is the point.
  • Use calendar to pick dates.

shadcn CLI

Also installs (1)

  • manicat-foundation
Manual install(+)
registry.jsonjson
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "activity-heatmap",
  "type": "registry:ui",
  "title": "Activity heatmap",
  "description": "See a year of activity at a glance, one square per day.",
  "categories": [
    "data"
  ],
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "https://manicat.viraza.net/r/manicat-foundation.json"
  ],
  "files": [
    {
      "path": "registry/components/activity-heatmap/activity-heatmap.tsx",
      "type": "registry:component",
      "target": "@components/manicat/activity-heatmap/activity-heatmap.tsx"
    },
    {
      "path": "registry/components/activity-heatmap/activity-heatmap.module.css",
      "type": "registry:component",
      "target": "@components/manicat/activity-heatmap/activity-heatmap.module.css"
    }
  ],
  "docs": "Docs and live preview: https://uiarc.dev/components/activity-heatmap",
  "meta": {
    "tier": "free",
    "kind": "component",
    "docs": "https://manicat.viraza.net/components/activity-heatmap",
    "markdown": "https://uiarc.dev/components/activity-heatmap/markdown",
    "tags": [
      "data",
      "chart",
      "calendar"
    ]
  }
}

Usage

Install the component, then copy this into your app. Change the content and the props to fit your screen.

example.tsxtsx
"use client";

import { ActivityHeatmap } from "@/components/manicat/activity-heatmap/activity-heatmap";
import type { ActivityDay } from "@/components/manicat/activity-heatmap/activity-heatmap";

function iso(date: Date): string {
  return date.toISOString().slice(0, 10);
}

const heatmapDays: ActivityDay[] = Array.from({ length: 140 }, (_, index) => {
  const date = new Date(Date.UTC(2026, 4, 4, 12));
  date.setUTCDate(date.getUTCDate() + index);
  const count = Math.floor(((index * 37) % 31) * (index % 5 === 0 ? 1.4 : 0.6));
  return { date: iso(date), count: Math.min(count, 26) };
});

export function ActivityHeatmapExample() {
  // Size containers have no width of their own: give the demo the whole row.
  return <div className="w-full"><ActivityHeatmap days={heatmapDays} label="Commits" period="May to August 2026" unit={{ one: "commit", other: "commits" }} /></div>;
}

Examples

Real screens rather than isolated props. Switch to Code to copy a whole example into your project.

Contribution graph A GitHub-style calendar of commits with a plural-aware unit in the tooltip.

Contribution graph A GitHub-style calendar of commits with a plural-aware unit in the tooltip. preview

commits in 1,487 commits in May to August 2026

Darker squares mean more commits. Levels: no commits, 1 to 7 commits, 8 to 13 commits, 14 to 20 commits, 21 or more commits.

Keyboard interactions

KeysAction
ArrowUp / ArrowDownMoves one day back or forward.
ArrowLeft / ArrowRightMoves one week back or forward.
Home / EndJumps to the first or last day.
Enter / SpaceSelects the focused day.
EscapeHides the tooltip.

Accessibility

  • The grid is role="grid" with labelled gridcells and a hidden legend explaining the levels.
  • Legend swatches are toggle buttons with aria-pressed that highlight days of one level.
  • The total and hovered day are announced through status and polite live regions; the tooltip itself is aria-hidden.

Motion

  • Cells wave in once on view and a new range recolors the grid in a sweep.
  • The tooltip glides between cells and its text rolls; the total counts to new values.
  • Reduced motion drops the wave, sweep, and glide in favor of short fades.

Responsive behavior

  • Cells scale between 9px and 15px with the container, then the grid scrolls horizontally from the newest week.
  • Below a 440px container the caption switches to a short form.
  • On touch the tooltip lingers after a tap instead of hiding at once.

Performance

  • Every day is its own cell, so a year is about 370 elements; the reveal wave is capped in total time.
  • One shared tooltip serves the whole grid.

Notes for AI

  • Use when rhythm, streaks, and quiet weeks matter more than exact comparison. Use bar-chart when the exact comparison is the point.
  • Pass a range switch through actions and swap days to change the period.