Activity heatmap

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

pnpm dlx shadcn@latest add @uiarc/activity-heatmap
Live · keyboard ready

contributions in 1,170 contributions in 2025

Darker squares mean more contributions. Levels: no contributions, 1 to 3 contributions, 4 to 6 contributions, 7 to 10 contributions, 11 or more contributions.

Tuesday, March 18: 4 commits, 2 pull requests and 2 reviews, mostly in design-tokens.

  • Daily rhythm over a long range, like contributions or workouts.
  • Views where streaks and quiet weeks matter more than exact comparison.
  • Use bar-chart when the exact comparison is the point.
  • Use calendar to pick dates.

Installation

Add Activity heatmap with the shadcn CLI, or copy the source by hand.

pnpm dlx shadcn@latest add @uiarc/activity-heatmap

Adds the component and its local dependencies, and installs motion. First time? Add the @uiarc registry to components.json, or use the full URL:

example.tsx
import { useState } from "react";import { ActivityHeatmap } from "@/registry/components/activity-heatmap/activity-heatmap"; export function Contributions({ days }: { days: { date: string; count: number }[] }) {  const [selected, setSelected] = useState<string | null>(null);  return (    <ActivityHeatmap days={days} label="Contributions in 2026" period="2026" selectedDate={selected} onSelectDate={setSelected} />  );}

ActivityHeatmap

A contribution style calendar with one tinted square per day and a hover or focus tooltip.

PropTypeDefaultDescription
daysRequired{ date: string; count: number }[]–One entry per day as YYYY-MM-DD, oldest first. Missing days count as zero.
labelRequiredstring–Accessible name for the grid, such as "Contributions in 2025".
periodRequiredstring–Finishes the summary: "1,284 contributions in {period}".
unit{ one: string; other: string }{ one: "contribution", other: "contributions" }Nouns for the count.
thresholds[number, number, number]–Upper bounds for levels one to three. Defaults to quarters of the busiest day.
weekStartsOn0 | 10Sunday or Monday as the first row.
selectedDatestring | nullnullDay drawn with a selection ring.
onSelectDate(date: string) => void–Called on click, Enter, or Space.
actionsReactNode–Controls beside the summary, such as a range switch.
localestring"en-US"Formatting locale.
classNamestring–Class for the root.
ArrowUporArrowDown
Moves one day back or forward.
ArrowLeftorArrowRight
Moves one week back or forward.
HomeorEnd
Jumps to the first or last day.
EnterorSpace
Selects the focused day.
Escape
Hides the tooltip.
  • 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.
  • 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.
  • 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.
  • 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

Give your coding assistant the Markdown reference instead of screenshots.

  • 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.

The full library index for assistants is at /llms.txt.