Marble UI
← Components

Calendar Heatmap / Contributions

Preview: 26 weeks

Contribution-style grid: one column per week, five steps of one hue, zero on the sunken surface. Day tooltips on hover, or focus + arrow keys (Left/Right a week, Up/Down a day).

Import

import { CalendarHeatmap } from '@marbleui/ui'

Props

<CalendarHeatmap>

PropTypeDefaultDescription
summary *requiredstring—
values *requiredRecord<string, number>—ISO date (YYYY-MM-DD) → count.
classNamestring—
endDate—Last day shown (defaults to today).
label(count: number, date: Date) => string(c, d) => `${c} on ${d.toDateString()}`
weeksnumber26

Usage notes

Daily counts as a contribution-style grid: one column per week, one row per weekday. For two arbitrary category axes use Heatmap.

<CalendarHeatmap
  weeks={26}
  values={{ '2026-10-26': 4, '2026-10-27': 9, '2026-10-28': 2 }}
  summary="412 contributions in the last 26 weeks, mostly on weekdays."
  label={(count, date) => `${count} contributions on ${date.toLocaleDateString()}`}
/>
  • values maps YYYY-MM-DD to a count; missing days count as zero. The grid ends at end (default today) and each column starts on Sunday.
  • Zero is drawn on the sunken surface; other counts fall into four steps relative to the largest count in values, including days outside the visible range.
  • There is no title and no “Show data” table. The per-day label text shows on hover, or on keyboard: Tab focuses the grid, Up/Down step a day, Left/Right a week, and a live region reads the day. Put the totals that matter in summary, or place a Table alongside.
  • Month names are always English. The default label reads like “3 on Wed Oct 28 2026”, so pass your own with units.
Example source · show
import { useMemo } from 'react'
import { CalendarHeatmap } from './calendar-heatmap'

function useContributions(weeks: number) {
  return useMemo(() => {
    const out: Record<string, number> = {}
    const d = new Date()
    for (let i = 0; i < weeks * 7; i++) {
      const day = new Date(d)
      day.setDate(d.getDate() - i)
      const wd = day.getDay()
      const n = Math.max(0, Math.round((wd === 0 || wd === 6 ? 1 : 6) * Math.abs(Math.sin(i * 1.7)) + (i % 9 === 0 ? 8 : 0) - 1))
      out[day.toISOString().slice(0, 10)] = n
    }
    return out
  }, [weeks])
}

function HalfYear() {
  const values = useContributions(26)
  return <CalendarHeatmap values={values} weeks={26} summary="Contributions over the last 26 weeks; most activity on weekdays." label={(c, d) => `${c} contributions on ${d.toLocaleDateString('en', { month: 'short', day: 'numeric' })}`} />
}

function Quarter() {
  const values = useContributions(13)
  return <CalendarHeatmap values={values} weeks={13} summary="Deploys over the last 13 weeks." label={(c) => `${c} deploys`} />
}

export const styles = { '26 weeks': HalfYear, '13 weeks': Quarter }

Source: packages/ui/src/calendar-heatmap/calendar-heatmap.tsx

Catalog

Category
charts
Priority
Pro
Primitive
custom