2

Heatmap Chart

PreviousNext

Versatile heatmaps with multiple variants including matrix, calendar, and radial displays.

4550556065702530510657075803540202530354000551015202530354010157075808540454045100556065202525308590951005100003060912151821HourFriMonSatSunThuTueWedDay100.00.0
"use client"

import { HeatmapChart } from "@/components/ui/charts"

// Generate matrix data for hours vs days
const hours = ["00", "03", "06", "09", "12", "15", "18", "21"]
const days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]

const data = days.flatMap((day, dayIndex) =>
  hours.map((hour, hourIndex) => {
    // Create a pattern: more activity during work hours on weekdays
    const isWeekend = dayIndex >= 5
    const isWorkHour = hourIndex >= 2 && hourIndex <= 5
    const baseValue = isWeekend ? 20 : isWorkHour ? 80 : 30
    const variance = ((dayIndex * 7 + hourIndex) % 10) * 5

    return {
      x: hour,
      y: day,
      value: Math.max(0, baseValue + variance - 25),
    }
  })
)

export function HeatmapChartDemo() {
  return (
    <div className="w-full max-w-3xl">
      <HeatmapChart
        data={data}
        variant="matrix"
        xAxisLabel="Hour"
        yAxisLabel="Day"
        colorTheme="blue"
        showValues
        cellRadius={4}
        height={300}
        valueFormat={(v) => v.toString()}
      />
    </div>
  )
}

About

The Heatmap Chart displays data with color intensity representing values. It supports multiple variants for different use cases.

Installation

pnpm dlx shadcn@latest add https://ui.simplifying.ai/r/heatmap-chart.json

Variants

Matrix (Default)

Traditional grid heatmap with rows and columns.

4550556065702530510657075803540202530354000551015202530354010157075808540454045100556065202525308590951005100003060912151821HourFriMonSatSunThuTueWedDay100.00.0
"use client"

import { HeatmapChart } from "@/components/ui/charts"

// Generate matrix data for hours vs days
const hours = ["00", "03", "06", "09", "12", "15", "18", "21"]
const days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]

const data = days.flatMap((day, dayIndex) =>
  hours.map((hour, hourIndex) => {
    // Create a pattern: more activity during work hours on weekdays
    const isWeekend = dayIndex >= 5
    const isWorkHour = hourIndex >= 2 && hourIndex <= 5
    const baseValue = isWeekend ? 20 : isWorkHour ? 80 : 30
    const variance = ((dayIndex * 7 + hourIndex) % 10) * 5

    return {
      x: hour,
      y: day,
      value: Math.max(0, baseValue + variance - 25),
    }
  })
)

export function HeatmapChartMatrixDemo() {
  return (
    <div className="w-full max-w-3xl">
      <HeatmapChart
        data={data}
        variant="matrix"
        xAxisLabel="Hour"
        yAxisLabel="Day"
        colorTheme="blue"
        showValues
        cellRadius={4}
        height={300}
        valueFormat={(v) => v.toString()}
      />
    </div>
  )
}

Calendar

GitHub-style contribution calendar heatmap.

JanFebMarAprMayJunJulAugSepOctNovDecSUNTUETHUSATLessMore
"use client"

import { HeatmapChart } from "@/components/ui/charts"

// Generate sample contribution data for the past year
function generateContributionData() {
  const data = []
  const now = new Date()
  const oneYearAgo = new Date(now)
  oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1)

  // Use a deterministic seed based on date for consistent results
  const seed = (dateStr: string) => {
    let hash = 0
    for (let i = 0; i < dateStr.length; i++) {
      const char = dateStr.charCodeAt(i)
      hash = (hash << 5) - hash + char
      hash = hash & hash
    }
    return Math.abs(hash)
  }

  const current = new Date(oneYearAgo)
  while (current <= now) {
    const dateStr = current.toISOString().split("T")[0]
    const seededRandom = seed(dateStr) / 2147483647

    // Create a pattern: more activity on weekdays, less on weekends
    const dayOfWeek = current.getDay()
    const isWeekend = dayOfWeek === 0 || dayOfWeek === 6
    const baseActivity = isWeekend ? 0.3 : 0.7

    // Random contribution count
    const activityLevel = seededRandom * baseActivity
    if (activityLevel > 0.2) {
      const value = Math.floor(activityLevel * 15) + 1
      data.push({
        date: new Date(current),
        value,
      })
    }

    current.setDate(current.getDate() + 1)
  }

  return data
}

const contributionData = generateContributionData()

export function HeatmapChartCalendarDemo() {
  return (
    <div className="w-full">
      <HeatmapChart
        data={contributionData}
        variant="calendar"
        colorTheme="green"
        cellSize={11}
        cellGap={3}
        cellRadius={2}
        legend={{
          show: true,
          lessLabel: "Less",
          moreLabel: "More",
        }}
      />
    </div>
  )
}

Radial

Circular/polar heatmap for time-based data with hours around the circumference and days as concentric rings.

12am1am2am3am4am5am6am7am8am9am10am11am12pm1pm2pm3pm4pm5pm6pm7pm8pm9pm10pm11pmSundayMondayTuesdayWednesdayThursdayFridaySaturdayLowHigh
"use client"

import { HeatmapChart } from "@/components/ui/charts"

// Generate sample hourly activity data for each day of the week
function generateRadialData() {
  const data: { day: number; hour: number; value: number }[] = []
  const days = [0, 1, 2, 3, 4, 5, 6] // Sunday to Saturday
  const hours = Array.from({ length: 24 }, (_, i) => i) // 0-23

  // Create a deterministic seed
  const seed = (day: number, hour: number) => {
    const hash = (day * 24 + hour) * 2654435761
    return (hash >>> 0) / 4294967296
  }

  days.forEach((day) => {
    hours.forEach((hour) => {
      const random = seed(day, hour)

      // Create realistic patterns:
      // - More activity during work hours (9-17) on weekdays
      // - Less activity on weekends
      // - Very low activity at night (0-6)
      const isWeekend = day === 0 || day === 6
      const isWorkHour = hour >= 9 && hour <= 17
      const isNight = hour >= 0 && hour <= 6
      const isEvening = hour >= 18 && hour <= 22

      let baseActivity = 0.3

      if (isNight) {
        baseActivity = 0.05
      } else if (isWorkHour && !isWeekend) {
        baseActivity = 0.7
      } else if (isEvening) {
        baseActivity = 0.5
      } else if (isWeekend) {
        baseActivity = 0.4
      }

      const value = Math.floor(random * baseActivity * 100)

      if (value > 5) {
        data.push({
          day,
          hour,
          value,
        })
      }
    })
  })

  return data
}

const radialData = generateRadialData()

export function HeatmapChartRadialDemo() {
  return (
    <div className="mx-auto w-full max-w-xl">
      <HeatmapChart
        data={radialData}
        variant="radial"
        colorTheme="heat"
        width={500}
        height={500}
        cellGap={1}
        legend={{
          show: true,
          lessLabel: "Low",
          moreLabel: "High",
        }}
      />
    </div>
  )
}

Color Themes

Built-in color themes with automatic dark mode support.

JanFebMarAprMayJunJulAugSepOctNovDecSUNTUETHUSATLessMore
"use client"

import * as React from "react"

import { HeatmapChart } from "@/components/ui/charts"

// Generate sample contribution data
function generateContributionData() {
  const data = []
  const now = new Date()
  const sixMonthsAgo = new Date(now)
  sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6)

  const seed = (dateStr: string) => {
    let hash = 0
    for (let i = 0; i < dateStr.length; i++) {
      const char = dateStr.charCodeAt(i)
      hash = (hash << 5) - hash + char
      hash = hash & hash
    }
    return Math.abs(hash)
  }

  const current = new Date(sixMonthsAgo)
  while (current <= now) {
    const dateStr = current.toISOString().split("T")[0]
    const seededRandom = seed(dateStr) / 2147483647

    if (seededRandom > 0.3) {
      const value = Math.floor(seededRandom * 12) + 1
      data.push({
        date: new Date(current),
        value,
      })
    }

    current.setDate(current.getDate() + 1)
  }

  return data
}

const themes = [
  "green",
  "blue",
  "purple",
  "orange",
  "red",
  "gray",
  "pink",
] as const

export function HeatmapChartThemesDemo() {
  const [activeTheme, setActiveTheme] =
    React.useState<(typeof themes)[number]>("green")
  const contributionData = React.useMemo(() => generateContributionData(), [])

  return (
    <div className="w-full space-y-4">
      <div className="flex flex-wrap gap-2">
        {themes.map((theme) => (
          <button
            key={theme}
            onClick={() => setActiveTheme(theme)}
            className={`rounded-md px-3 py-1 text-sm font-medium capitalize transition-colors ${
              activeTheme === theme
                ? "bg-primary text-primary-foreground"
                : "bg-muted text-muted-foreground hover:bg-muted/80"
            }`}
          >
            {theme}
          </button>
        ))}
      </div>
      <HeatmapChart
        data={contributionData}
        variant="calendar"
        colorTheme={activeTheme}
        cellSize={10}
        cellGap={3}
        cellRadius={2}
        height={140}
        legend={{
          show: true,
          lessLabel: "Less",
          moreLabel: "More",
        }}
      />
    </div>
  )
}

Available themes: green, blue, purple, orange, red, gray, pink, heat, thermal

Usage

Matrix Variant

import { HeatmapChart } from "@/components/ui/charts"
 
const data = [
  { x: "Mon", y: "9am", value: 10 },
  { x: "Mon", y: "10am", value: 25 },
  { x: "Tue", y: "9am", value: 15 },
  { x: "Tue", y: "10am", value: 30 },
]
 
export function MatrixExample() {
  return (
    <HeatmapChart data={data} variant="matrix" colorTheme="blue" showValues />
  )
}

Calendar Variant

import { HeatmapChart } from "@/components/ui/charts"
 
const contributionData = [
  { date: new Date("2024-01-15"), value: 5 },
  { date: new Date("2024-01-16"), value: 12 },
  { date: "2024-01-17", value: 3 }, // String dates also work
]
 
export function CalendarExample() {
  return (
    <HeatmapChart
      data={contributionData}
      variant="calendar"
      colorTheme="green"
      cellSize={11}
      cellGap={3}
    />
  )
}

Radial Variant

import { HeatmapChart } from "@/components/ui/charts"
 
const activityData = [
  { day: 0, hour: 9, value: 45 }, // Sunday 9am
  { day: 1, hour: 14, value: 80 }, // Monday 2pm
  { day: "Tuesday", hour: "3pm", value: 65 }, // String formats work too
]
 
export function RadialExample() {
  return (
    <HeatmapChart
      data={activityData}
      variant="radial"
      colorTheme="heat"
      width={500}
      height={500}
    />
  )
}

API Reference

PropTypeDefaultDescription
dataDataPoint[]requiredData points for the heatmap
variant"matrix" "calendar" "radial" "compact""matrix"Display variant
colorThemestring"green"Color theme preset
colorScalestring[]-Custom color array (overrides theme)
showValuesbooleanfalseShow values in cells (matrix only)
cellRadiusnumber2Cell corner radius
cellGapnumber3Gap between cells
cellSizenumber12Cell size (calendar/radial)
showMonthLabelsbooleantrueShow month labels (calendar)
showWeekdayLabelsbooleantrueShow weekday labels (calendar)
weekStartsOn0 or 10Week start (0=Sunday, 1=Monday)
legendobjectsee belowLegend configuration
onCellClickfunction-Cell click handler

Data Types

HeatmapDataPoint (Matrix)

interface HeatmapDataPoint {
  x: string | number
  y: string | number
  value: number
}

CalendarDataPoint (Calendar)

interface CalendarDataPoint {
  date: Date | string
  value: number
}

RadialHeatmapDataPoint (Radial)

interface RadialHeatmapDataPoint {
  day: number | string // 0-6 or day name
  hour: number | string // 0-23 or "12am", "1pm", etc.
  value: number
}

Notes

  • The heatmap chart supports three distinct variants: matrix, calendar, and radial
  • Matrix variant is ideal for visualizing correlations and patterns in two-dimensional data
  • Calendar variant mimics GitHub's contribution graph, perfect for tracking daily activities
  • Radial variant displays time-based patterns in a circular layout with hours and days
  • All variants support multiple color themes with automatic dark mode support
  • Built-in themes include: green, blue, purple, orange, red, gray, pink, heat, and thermal
  • Custom color scales can override theme presets for full control over appearance
  • The calendar variant supports both Date objects and ISO date strings
  • The radial variant accepts both numeric and string formats for days and hours
  • Cell interactions can be captured through the onCellClick handler
  • Color intensity automatically scales based on the min/max values in the dataset
  • The component handles responsive sizing and maintains aspect ratios across different screen sizes