Skip to content

Activity Heatmap

DataMIT
View in Storybook

GitHub-style yearly activity contribution grid with hover tooltips.

bun add @otfdashkit/ui

Preview

Activity Heatmap
Loading Storybook preview…

Code

import { ActivityHeatmap } from '@otfdashkit/ui'

const HEATMAP_DATA = (() => {
  const out: { date: string; value: number }[] = []
  let seed = 42
  const rng = () => { seed = (seed * 9301 + 49297) % 233280; return seed / 233280 }
  const today = new Date('2026-04-29')
  const start = new Date(today); start.setDate(start.getDate() - 365)
  const cursor = new Date(start)
  while (cursor <= today) {
    const dow = cursor.getDay()
    const isWeekend = dow === 0 || dow === 6
    out.push({
      date: `${cursor.getFullYear()}-${String(cursor.getMonth() + 1).padStart(2, '0')}-${String(cursor.getDate()).padStart(2, '0')}`,
      value: Math.max(0, Math.round(rng() * (isWeekend ? 6 : 22))),
    })
    cursor.setDate(cursor.getDate() + 1)
  }
  return out
})()

export function Example() {
  return (
    <div className="p-4 max-w-5xl">
      <ActivityHeatmap
        caption="Activity"
        total={1_847_392}
        data={HEATMAP_DATA}
        endDate={new Date('2026-04-29')}
        stats={[
          { label: 'Most Active Month', value: 'March' },
          { label: 'Most Active Day',   value: 'Mar 18, 2026' },
          { label: 'Longest Streak',    value: '64d' },
          { label: 'Current Streak',    value: '21d' },
        ]}
      />
    </div>
  )
}

Free with the SDK. This component ships in @otfdashkit/ui under the MIT license.

Props

Props declared in the ActivityHeatmap source. Inherited HTML attributes are not listed.

PropTypeDefaultDescription
datarequiredHeatmapDay[]Daily values for the heatmap
totalrequiredstring | numberThe big number above the heatmap (e.g. total events)
captionstring'Activity'Caption above the total — e.g. "Activity"
cellSizenumber11Cell size in px. Default 11.
centeredbooleantrueCenter the heatmap horizontally inside its scroll container. Default true.
classNamestring
colorScalestring[]Optional 5-step color scale. Defaults to primary opacity ramp.
endDateDateDate the heatmap should end on. Defaults to today.
filterValuestring
filtersFilterOption[]Optional filter — renders a ToggleGroup at the top right
formatTooltip(day: HeatmapDay) => stringTooltip formatter passed to Heatmap
gapnumber3Gap between cells in px. Default 3.
onFilterChange(value: string) => void
statsSummaryStat[]4 summary stats shown below the heatmap (e.g. Most Active Month / Day / Longest / Current Streak)
weeksnumber53Number of week columns. Default 53.