A flexible, customizable calendar heatmap component for React, inspired by GitHub's contribution graph.
- 📅 Flexible Date Ranges - Display any date range with automatic week alignment
- 🎨 Customizable Colors - Define your own color scales and thresholds
- 🌓 Light/Dark Mode - Built-in theme support
- 📱 Responsive - Horizontal scrolling for large date ranges
- 🎯 Interactive - Click and hover handlers for cells
- 🔧 Highly Customizable - Override styles and classes for any element
- 📊 Smart Labels - Automatic month label positioning with overlap prevention
- 🌍 Week Start Options - Start weeks on any day (Sunday, Monday, etc.)
- 💡 TypeScript - Full TypeScript support with exported types
# If publishing as npm package
npm install @your-org/react-heatmap
# Or copy the component files to your project
cp -r components/common/Heatmap /your-project/components/import { Heatmap } from '@/components/common/Heatmap';
function MyComponent() {
const data = [
{ date: '2024-01-01', count: 5 },
{ date: '2024-01-02', count: 12 },
{ date: '2024-01-03', count: 0 },
// ... more data
];
return (
<Heatmap
values={data}
startDate="2024-01-01"
endDate="2024-12-31"
/>
);
}| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
values |
HeatmapValue[] |
✅ | - | Array of { date: string, count: number } objects |
startDate |
string |
✅ | - | Start date in ISO format (YYYY-MM-DD) |
endDate |
string |
✅ | - | End date in ISO format (YYYY-MM-DD) |
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
showMonthLabels |
boolean |
❌ | true |
Show month labels |
showWeekdayLabels |
boolean |
❌ | true |
Show weekday labels |
monthPlacement |
'top' | 'bottom' |
❌ | 'top' |
Position of month labels |
weekStart |
0-6 |
❌ | 0 |
Starting day of week (0=Sunday, 1=Monday, etc.) |
showLegend |
boolean |
❌ | true |
Show color legend |
emptyMessage |
string |
❌ | 'No data available...' |
Message shown when no data |
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
cellSize |
number |
❌ | 12 |
Size of each cell in pixels |
cellGap |
number |
❌ | 3 |
Gap between cells in pixels |
cellRadius |
number |
❌ | 2 |
Border radius for cells in pixels |
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
colorScale |
ColorScale |
❌ | GitHub greens | Color thresholds object |
surface |
'light' | 'dark' |
❌ | 'light' |
Theme mode |
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
className |
string |
❌ | - | Custom class for container |
classNames |
HeatmapClassNames |
❌ | - | Override classes for elements |
style |
CSSProperties |
❌ | - | Inline styles for container |
styles |
HeatmapStyles |
❌ | - | Override styles for elements |
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
tooltipFormatter |
(value) => string |
❌ | Default formatter | Custom tooltip content |
onClick |
(value, event) => void |
❌ | - | Cell click handler |
onMouseOver |
(value, event) => void |
❌ | - | Cell hover handler |
onMouseLeave |
(value, event) => void |
❌ | - | Cell leave handler |
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
monthLabels |
string[] |
❌ | ['Jan', 'Feb', ...] |
Custom month labels |
weekdayLabels |
string[] |
❌ | ['Sun', 'Mon', ...] |
Custom weekday labels |
<Heatmap
values={data}
startDate="2024-01-01"
endDate="2024-12-31"
colorScale={{
0: '#f0f0f0',
5: '#c6e48b',
10: '#7bc96f',
15: '#239a3b',
20: '#196127',
}}
/><Heatmap
values={data}
startDate="2024-01-01"
endDate="2024-12-31"
weekStart={1}
weekdayLabels={['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']}
/><Heatmap
values={data}
startDate="2024-01-01"
endDate="2024-12-31"
tooltipFormatter={(value) =>
`${value.count} contributions on ${new Date(value.date).toLocaleDateString()}`
}
/><Heatmap
values={data}
startDate="2024-01-01"
endDate="2024-12-31"
surface="dark"
/><Heatmap
values={data}
startDate="2024-01-01"
endDate="2024-12-31"
monthPlacement="bottom"
/><Heatmap
values={data}
startDate="2024-01-01"
endDate="2024-12-31"
showMonthLabels={false}
showWeekdayLabels={false}
showLegend={false}
/>interface HeatmapValue {
date: string; // ISO format: YYYY-MM-DD
count: number;
}
interface ColorScale {
[threshold: number]: string;
}
type TooltipFormatter = (value: HeatmapValue) => string;
type CellClickHandler = (value: HeatmapValue, event: MouseEvent<HTMLDivElement>) => void;
type CellHoverHandler = (value: HeatmapValue | null, event: MouseEvent<HTMLDivElement>) => void;The classNames prop allows you to override classes for specific elements:
<Heatmap
values={data}
startDate="2024-01-01"
endDate="2024-12-31"
classNames={{
container: 'my-container',
cell: 'my-cell',
cellActive: 'my-active-cell',
cellEmpty: 'my-empty-cell',
tooltip: 'my-tooltip',
legend: 'my-legend',
monthLabel: 'my-month-label',
weekdayLabel: 'my-weekday-label',
}}
/>The styles prop allows you to override inline styles:
<Heatmap
values={data}
startDate="2024-01-01"
endDate="2024-12-31"
styles={{
container: { backgroundColor: '#f5f5f5', padding: '20px' },
cell: { borderRadius: '4px' },
tooltip: { fontSize: '14px', padding: '8px 12px' },
legend: { justifyContent: 'center' },
}}
/>The default color scale uses GitHub-style greens:
{
0: '#ebedf0', // Empty/no activity
2: '#9be9a8', // Low activity
5: '#40c463', // Medium activity
10: '#30a14e', // High activity
20: '#216e39', // Very high activity
}- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
- The component efficiently handles large date ranges (multiple years)
- Uses
useMemofor expensive calculations - Horizontal scrolling for wide heatmaps
- Optimized re-renders with proper dependency arrays
- Semantic HTML structure
- Keyboard navigation support (when click handlers are provided)
- ARIA labels can be added via
classNamesand custom attributes - High contrast mode compatible
MIT
Inspired by:
Contributions are welcome! Please feel free to submit a Pull Request.
- Initial release
- Full TypeScript support
- Customizable color scales
- Light/Dark mode
- Interactive tooltips
- Click and hover handlers
- Smart month label positioning
- Week start customization
<Heatmap
values={data}
startDate="2024-01-01"
endDate="2024-12-31"
onClick={(value, event) => {
console.log(`Clicked on ${value.date} with ${value.count} activities`);
}}
/><Heatmap
values={data}
startDate="2024-01-01"
endDate="2024-12-31"
className="my-custom-heatmap"
classNames={{
cell: 'custom-cell',
tooltip: 'custom-tooltip',
}}
styles={{
container: { padding: '24px' },
cell: { borderRadius: '50%' },
}}
/>