Activity heatmap
See a year of activity at a glance, one square per day.
pnpm dlx shadcn@latest add @uiarc/activity-heatmapLive · 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-heatmapAdds 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.