Skip to content

Repository files navigation

React Heatmap Calendar (shadcn/ui)

License: MIT GitHub stars PRs Welcome

A GitHub-style heatmap calendar component built with React, Tailwind CSS, and shadcn/ui.

✔️ Copy-paste friendly ✔️ No charting libraries ✔️ Light & dark mode ✔️ Axis labels (months + weekdays) ✔️ Domain-agnostic (fitness, business, IoT, learning, etc.)


✨ Features

  • 📅 Calendar heatmap (GitHub-style)
  • 📆 Year selector — browse full calendar years with a built-in dropdown
  • 🧭 Axis labels
    • Months on top
    • Weekdays on the left
  • 🎨 Theme presets with copyable CSS variables
  • 🌗 Light / dark mode ready
  • 🧩 Domain-agnostic
    • fitness activity
    • business metrics
    • support tickets
    • learning progress
    • IoT events
  • 🧠 Smart data merging (duplicate dates are summed)
  • 🧪 Tooltips & click handlers
  • ⚡ No canvas, SVG, or heavy chart libraries

📸 Preview

React Heatmap Calendar preview React Heatmap Calendar preview Live demo available in the repository / website.


🚀 Installation

Option A: shadcn CLI (recommended)

Installs the component plus its card/tooltip dependencies in one step:

npx shadcn@latest add https://<your-deployed-domain>/r/heatmap-calendar.json

This registry item is served by this repo itself at /r/heatmap-calendar.json (built via npm run registry:build, which runs automatically before next build). If you deploy your own copy, set NEXT_PUBLIC_SITE_URL in your environment so the generated URL, sitemap, and Open Graph tags point at your real domain.

Option B: copy-paste

This component can also be copied directly into your project (like shadcn blocks).

1️⃣ Install dependencies

npm install clsx tailwind-merge lucide-react

2️⃣ Add required shadcn/ui components

npx shadcn@latest add card tooltip

3️⃣ Copy the component file into your project:

components/heatmap-calendar.tsx

No provider. No configuration. No build step.


🔧 API Reference

props

Prop Type Default Description
title string "Activity" Title displayed above the heatmap
data HeatmapDatum[] required Daily aggregated data
rangeDays number 365 Number of days to render (ending at endDate)
endDate Date new Date() Last date of the heatmap range
weekStartsOn 0 | 1 1 Week start (0 = Sunday, 1 = Monday)
cellSize number 12 Cell size in pixels
cellGap number 3 Gap between cells in pixels
axisLabels boolean | AxisLabelConfig true Show or configure axis labels
legend boolean | LegendConfig true Show or configure the legend
palette string[] — Custom color palette for intensity levels
levelClassNames string[] semantic defaults Tailwind classes for intensity levels
scale "fixed" | "quantile" "fixed" How values map to levels (see below)
thresholds number[] [2, 5, 10] Cutoffs used when scale="fixed"
getLevel (value: number) => number — Full custom value → level mapping
year number — Show this calendar year (controlled)
defaultYear number — Initial calendar year (uncontrolled)
onYearChange (year: number) => void — Called when the year changes
yearSelector boolean | YearSelectorConfig false Show a built-in year dropdown
onCellClick (cell: HeatmapCell) => void — Called when a cell is clicked
renderTooltip (cell: HeatmapCell) => ReactNode — Custom tooltip renderer
renderLegend (args) => ReactNode — Fully custom legend renderer
className string — Additional class names for the card

📊 Data format

The heatmap expects daily aggregated data.

export type HeatmapDatum = {
  date: string | Date; // "YYYY-MM-DD" recommended
  value: number; // daily intensity
  meta?: unknown; // optional metadata
};

Example

const data = [
  { date: "2025-01-01", value: 3 },
  { date: "2025-01-02", value: 0 },
  { date: "2025-01-03", value: 8 },
];

Notes

  • Duplicate dates are automatically merged (values are summed)
  • Use "YYYY-MM-DD" strings to avoid timezone issues
  • Values can represent anything: minutes, orders, tickets, events, etc.

🧩 Basic usage

import { HeatmapCalendar } from "@/components/heatmap-calendar"

export default function Example() {
  return (
    <HeatmapCalendar
      title="Activity"
      data={data}
      axisLabels
    />
  )
}

🧭 Axis labels (months + weekdays)

Axis labels are enabled by default and show:

  • 📅 Months on top
  • 📆 Weekdays on the left
<HeatmapCalendar data={data} axisLabels />

Customize axis labels

<HeatmapCalendar
  data={data}
  axisLabels={{
    showMonths: true,
    showWeekdays: true,
    weekdayIndices: [1, 3, 5], // Mon / Wed / Fri
    monthFormat: "short",      // "short" | "long" | "numeric"
    minWeekSpacing: 3,
  }}
/>

Disable completely:

<HeatmapCalendar data={data} axisLabels={false} />

🎨 Color scale

The default buckets ([2, 5, 10]) are shaped for GitHub-commit-style counts. For other units (minutes, dollars, tickets...), either set your own cutoffs or let the buckets be derived from your data automatically:

// Fixed unit? Set your own cutoffs.
<HeatmapCalendar data={data} thresholds={[50, 150, 300]} />

// Unknown/variable unit? Derive buckets from the data itself.
<HeatmapCalendar data={data} scale="quantile" />

// Full control.
<HeatmapCalendar data={data} getLevel={(value) => (value > 100 ? 4 : value > 10 ? 2 : 0)} />

📆 Year selection

Switch between full calendar years (Jan 1 – Dec 31), GitHub-profile style, with a built-in dropdown next to the title:

<HeatmapCalendar data={data} axisLabels yearSelector />

Uncontrolled with a custom starting year and year list:

<HeatmapCalendar
  data={data}
  defaultYear={2024}
  yearSelector={{ years: [2024, 2023, 2022] }}
/>

Fully controlled (you own the state):

const [year, setYear] = React.useState(2025)

<HeatmapCalendar data={data} year={year} onYearChange={setYear} yearSelector />

Dates after today are rendered as "Upcoming" and are not clickable.

🖱️ Tooltips & interaction

<HeatmapCalendar
  data={data}
  onCellClick={(cell) => {
    console.log(cell.date, cell.value, cell.meta)
  }}
  renderTooltip={(cell) => (
    <div>
      <div className="font-medium">{cell.value} events</div>
      <div className="text-muted-foreground">{cell.label}</div>
    </div>
  )}
/>

🤝 Contributing

Contributions are welcome!

  • Bug fixes
  • Performance improvements
  • Documentation improvements
  • New examples

Feel free to open an issue or pull request.


📄 License

MIT © Minh (Marcus) Nguyen


Made with ❤️ by Minh (Marcus) Nguyen

About

A reusable React heatmap calendar built with shadcn/ui and ailwind CSS. Visualize daily activity, fitness data, business metrics, IoT events, or learning progress with a GitHub-style heatmap.

Topics

Resources

Stars

39 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages