Heat Calendar

Shows daily activity on a calendar.

Commit activity
Drag across days, or use arrows + Enter, to total a range
Sep 14 – Sep 27·57 commits· 11/14 active days

Installation

pnpm dlx shadcn@latest add @paneui/heat-calendar

Usage

Composition

Examples

Monday Start

Full Year

Custom Composition

Arrange the parts yourself when you need another layout or your own tooltip body.

Default Selection

Selecting ranges

Drag across days with the pointer. With the keyboard, the arrow keys move between days, Enter or Space starts and then ends a range, Shift + arrows extend it and Esc clears.

Dates

All dates are UTC YYYY-MM-DD strings. Days after endDate render as empty placeholders, and the grid starts scrolled to the latest week.

API Reference

HeatCalendar

Root and context. Without children it renders the grid with tooltip, then summary and legend. Also accepts every prop of div.

data*
{ date: string; count: number }[]
Daily counts; duplicates are summed.
endDate= latest date in data
string
Last UTC day shown.
weeks= 26
number
Number of week columns.
weekStartsOn= 0
0 | 1
0 = Sunday, 1 = Monday.
unit= the locale's
string
Noun after every count.
color= "var(--chart-4, var(--success))"
string
The single hue; strength maps to count.
maxCount= visible maximum
number
Count that maps to the strongest level.
cellSize= 12
number
Cell size in px.
gap= 3
number
Gap between cells in px.
label= the locale's
string
Accessible name.
selection
{ start: string; end?: string } | null
Controlled selected range.
defaultSelection= null
{ start: string; end?: string } | null
Initially selected range.
onSelectionChange
(selection) => void
Called when a range is started, finished or cleared.
children
ReactNode
Custom composition of parts.
PropTypeDefaultDescription
data*{ date: string; count: number }[]—Daily counts; duplicates are summed.
endDatestringlatest date in dataLast UTC day shown.
weeksnumber26Number of week columns.
weekStartsOn0 | 100 = Sunday, 1 = Monday.
unitstringthe locale'sNoun after every count.
colorstring"var(--chart-4, var(--success))"The single hue; strength maps to count.
maxCountnumbervisible maximumCount that maps to the strongest level.
cellSizenumber12Cell size in px.
gapnumber3Gap between cells in px.
labelstringthe locale'sAccessible name.
selection{ start: string; end?: string } | null—Controlled selected range.
defaultSelection{ start: string; end?: string } | nullnullInitially selected range.
onSelectionChange(selection) => void—Called when a range is started, finished or cleared.
childrenReactNode—Custom composition of parts.

HeatCalendarGrid

The scrollable day grid with month and weekday labels.

children= <HeatCalendarTooltip />
ReactNode
Overlay rendered over the grid.
className
string
Classes for the outer element.
PropTypeDefaultDescription
childrenReactNode<HeatCalendarTooltip />Overlay rendered over the grid.
classNamestring—Classes for the outer element.
data-level
On each cell: 0 (empty) to 4 (strongest).
data-selected
On cells inside the selected range.
Data attributeDescription
data-levelOn each cell: 0 (empty) to 4 (strongest).
data-selectedOn cells inside the selected range.

HeatCalendarTooltip

children
(day: { date: string; time: number; count: number; level: number }) => ReactNode
Custom body for the hovered or focused day.
className
string
Classes for the tooltip.
PropTypeDefaultDescription
children(day: { date: string; time: number; count: number; level: number }) => ReactNode—Custom body for the hovered or focused day.
classNamestring—Classes for the tooltip.

HeatCalendarSummary

Total for the period, or for the selected range with active days and Clear. Also accepts every prop of div.

HeatCalendarLegend

Less-to-more swatch scale. Also accepts every prop of div.

useHeatCalendar

Reads the root context (days, totals, selection, colours) to build your own parts.

generateHeatCalendarData

Deterministic demo data with quieter weekends, called as generateHeatCalendarData(rand, options).

rand*
() => number
Random source (first argument).
endDate= "2026-10-13"
string
Last day generated.
days= 371
number
Number of days.
intensity= 9
number
Rough maximum daily count.
PropTypeDefaultDescription
rand*() => number—Random source (first argument).
endDatestring"2026-10-13"Last day generated.
daysnumber371Number of days.
intensitynumber9Rough maximum daily count.