Activity Graph
GitHub-style activity heatmap that visualizes daily counts as a color-intensity grid with month labels, day labels, and a Less/More legend. Includes a helper to fetch real GitHub contribution data.
Preview
1,016 contributions in the last year
Installation
$ shadcn add https://ui.justinlevine.me/r/activity-graph.jsonUsage
import { ActivityGraph } from "@/components/activity-graph"<ActivityGraph data={[{ date: "2026-03-11", count: 5 }, ...]} />Pass an array of { date: string; count: number } entries. The component builds the trailing-week grid automatically. Dates not present in the array render as zero-count cells.
Playground
Props
import { ActivityGraph } from "@/components/activity-graph"
<ActivityGraph />GitHub helper
The included fetchGitHubContributions function scrapes the public GitHub contributions page for any username — no API key required. It returns per-day counts and the yearly total.
import { fetchGitHubContributions } from "@/lib/github"const contributions = await fetchGitHubContributions("octocat")<ActivityGraph data={contributions.entries} />The response is cached for 1 hour via Next.js ISR. Works as a server-side call in a server component or route handler.
Examples
Activity patterns
Sparse activity
Half year (26 weeks)
Custom color scales
Blue
Amber
Purple
Fixed block size
Fixed 14px (scrollable if wider than container)
API Reference
ActivityGraph
ActivityEntry
fetchGitHubContributions
Notes
- Client component. Uses
"use client"for a ResizeObserver that auto-sizes blocks to fit the container. Hover tooltips use nativetitleattributes. - Auto-fit. By default the block size is computed from the container width so the graph always fits without scrolling. Pass a fixed
blockSizeto opt into horizontal scrolling instead. - GitHub scraping. The fetch helper scrapes GitHub's public contributions HTML page. No API key needed. Cached for 1 hour via ISR. If GitHub changes their markup, the parser may need updating.
- Intensity mapping. Counts are mapped to four non-zero levels relative to the maximum count in the data. The thresholds are 25%, 50%, 75%, and 100% of the max.
- No dependencies. This component uses only React, Tailwind, and the
cnutility. No charting libraries or external packages.