Heatmap

A heatmap component to visualize data density over time.

Note: The Heatmap is a fully custom component with no external dependencies — built as a self-contained JavaScript component exposed through a Python API for use in Reflex.

Installation

Copy the following code into your app directory.

uv

uv run buridan add component heatmap
from components.ui.component import heatmap

Anatomy

Use the following composition to build a Heatmap component.

heatmap()

Examples

GitHub-Style HeatMap

Classic GitHub contribution graph — discrete mode, default green scale.

import random
from datetime import date, timedelta

from components.ui.heatmap import heatmap


def heatmap_github():
    data = []

    d = date(2025, 1, 1)
    while d <= date(2025, 12, 31):
        if random.random() > 0.3:
            data.append({"date": str(d), "value": random.randint(1, 20)})
        d += timedelta(days=1)

    return heatmap(
        data=data,
        start_date="2025-01-01",
        end_date="2025-11-30",
        color_mode="discrete",
        value_label="contributions",
        cell_size=14,
        root_class="scrollbar-none",
    )

Interpolated HeatMap

Set the interpolation prop to linear and pass in custom colors for continuous data visualization.

from components.ui.heatmap import heatmap


def heatmap_blue_linear():
    data = [
        {"date": "2025-01-05", "value": 2},
        {"date": "2025-01-12", "value": 9},
        {"date": "2025-02-01", "value": 4},
        {"date": "2025-02-20", "value": 15},
        {"date": "2025-03-10", "value": 6},
        {"date": "2025-04-04", "value": 18},
        {"date": "2025-05-15", "value": 3},
        {"date": "2025-06-01", "value": 11},
        {"date": "2025-07-22", "value": 20},
        {"date": "2025-08-08", "value": 7},
        {"date": "2025-09-30", "value": 14},
        {"date": "2025-10-10", "value": 5},
        {"date": "2025-11-11", "value": 19},
        {"date": "2025-12-25", "value": 1},
    ]

    return heatmap(
        data=data,
        start_date="2025-01-01",
        end_date="2025-12-31",
        color_mode="interpolate",
        min_color="#dbeafe",
        max_color="#1d4ed8",
        interpolation="linear",
        value_label="events",
        cell_size=14,
        root_class="scrollbar-none",
    )

Square Root HeatMap

Set the interpolation prop to sqrt and pass in custom colors. sqrt scaling makes low values more visible — good when most activity is sparse.

from components.ui.heatmap import heatmap


def heatmap_red_sqrt():
    data = [
        {"date": "2025-01-03", "value": 1},
        {"date": "2025-01-10", "value": 50},
        {"date": "2025-02-14", "value": 3},
        {"date": "2025-03-01", "value": 100},
        {"date": "2025-04-20", "value": 2},
        {"date": "2025-05-05", "value": 75},
        {"date": "2025-06-15", "value": 10},
        {"date": "2025-07-04", "value": 90},
        {"date": "2025-08-12", "value": 5},
        {"date": "2025-09-09", "value": 40},
        {"date": "2025-10-31", "value": 8},
        {"date": "2025-11-25", "value": 60},
        {"date": "2025-12-01", "value": 20},
    ]

    return heatmap(
        data=data,
        start_date="2025-01-01",
        end_date="2025-12-31",
        color_mode="interpolate",
        min_color="#fee2e2",
        max_color="#991b1b",
        interpolation="sqrt",
        value_label="errors",
        cell_size=14,
        root_class="scrollbar-none",
    )

Discrete Color Mode

Set the color_mode prop to discrete with a custom 5-level purple color scale. Pass any list of hex colors to color_scale — more levels = finer granularity.

from components.ui.heatmap import heatmap


def heatmap_purple_discrete():
    data = [
        {"date": "2025-01-07", "value": 1},
        {"date": "2025-01-14", "value": 2},
        {"date": "2025-01-21", "value": 3},
        {"date": "2025-01-28", "value": 4},
        {"date": "2025-02-04", "value": 5},
        {"date": "2025-02-11", "value": 3},
        {"date": "2025-03-01", "value": 1},
        {"date": "2025-03-15", "value": 4},
        {"date": "2025-04-10", "value": 2},
        {"date": "2025-05-20", "value": 5},
        {"date": "2025-06-30", "value": 3},
        {"date": "2025-09-01", "value": 4},
        {"date": "2025-10-15", "value": 2},
        {"date": "2025-11-20", "value": 5},
        {"date": "2025-12-10", "value": 1},
    ]

    return heatmap(
        data=data,
        start_date="2025-01-01",
        end_date="2025-12-31",
        color_mode="discrete",
        color_scale=["#f3e8ff", "#d8b4fe", "#a855f7", "#7e22ce", "#3b0764"],
        value_label="deploys",
        cell_size=14,
        root_class="scrollbar-none",
    )

Cell Size

Set the cell_size prop to a number to change cell size. Setting the interpolation to log is ideal when a few extreme values dominate — compresses the high end.

import reflex as rx

from components.ui.heatmap import heatmap


def heatmap_large_cells():
    data = [
        {"date": "2025-10-01", "value": 1},
        {"date": "2025-10-02", "value": 500},
        {"date": "2025-10-03", "value": 10},
        {"date": "2025-10-06", "value": 1000},
        {"date": "2025-10-07", "value": 5},
        {"date": "2025-10-08", "value": 250},
        {"date": "2025-10-09", "value": 3},
        {"date": "2025-10-10", "value": 750},
        {"date": "2025-10-13", "value": 50},
        {"date": "2025-10-14", "value": 900},
        {"date": "2025-10-15", "value": 20},
        {"date": "2025-10-16", "value": 600},
        {"date": "2025-10-17", "value": 8},
        {"date": "2025-10-20", "value": 400},
        {"date": "2025-10-21", "value": 2},
        {"date": "2025-10-22", "value": 800},
        {"date": "2025-10-23", "value": 15},
        {"date": "2025-10-24", "value": 350},
        {"date": "2025-10-27", "value": 100},
        {"date": "2025-10-28", "value": 700},
        {"date": "2025-10-29", "value": 30},
        {"date": "2025-10-30", "value": 950},
        {"date": "2025-10-31", "value": 12},
    ]

    return rx.el.div(
        *[
            heatmap(
                data=data,
                start_date="2025-10-01",
                end_date="2025-10-31",
                color_mode="interpolate",
                min_color="#f0fdf4",
                max_color="#14532d",
                interpolation="log",
                value_label="requests",
                cell_size=size,
                gap=4,
                root_class="scrollbar-none",
            )
            for size in [10, 15, 20]
        ],
        class_name="w-full flex flex-col sm:flex-row justify-center items-center gap-4",
    )

Minimal HeatMap

Set the show_dow and show_months props to False and lower the cell_size to get a compact, minimal heatmap. Useful as a sparkline-style indicator embedded in a dashboard card.

import random

from components.ui.heatmap import heatmap


def heatmap_compact():
    data = [
        {
            "date": f"2025-{str(m).zfill(2)}-{str(d).zfill(2)}",
            "value": random.randint(0, 10),
        }
        for m in range(1, 13)
        for d in range(1, 28)
    ]

    return heatmap(
        data=data,
        start_date="2025-01-01",
        end_date="2025-12-31",
        color_mode="discrete",
        show_dow=False,
        show_months=False,
        cell_size=10,
        gap=2,
        value_label="sales",
        root_class="scrollbar-none",
    )

API Reference

Props

PropTypeDefaultDescription
datalist[dict]Required. List of {"date": "YYYY-MM-DD", "value": int} entries. Dates not in the list render as empty cells.
start_datestrRequired. Start of the date range in YYYY-MM-DD format.
end_datestrRequired. End of the date range in YYYY-MM-DD format.
color_modestr"discrete"Color strategy. "discrete" maps values to a fixed color scale. "interpolate" blends smoothly between min_color and max_color.
color_scalelist[str]GitHub green scaleDiscrete mode only. List of hex color strings ordered from empty → max intensity. The first entry is always used for zero-value cells.
min_colorstr"#9be9a8"Interpolate mode only. Hex color for the lowest non-zero value.
max_colorstr"#216e39"Interpolate mode only. Hex color for the highest value.
interpolationstr"linear"Interpolate mode only. Scaling function applied before color blending. "linear" is uniform. "sqrt" makes low values more visible. "log" compresses high-end outliers.
cell_sizeint14Width and height of each cell in pixels. Also scales the border radius and font size proportionally.
gapint3Gap between cells in pixels.
show_dowboolTrueWhether to show Mon / Wed / Fri labels on the left axis.
show_monthsboolTrueWhether to show month labels along the top axis.
value_labelstr"contributions"Word appended to the count in the tooltip, e.g. "commits""3 commits · Mon Jan 1 2025".
root_classstr"w-full overflow-x-auto"Tailwind classes applied to the outermost div. Controls scroll and outer layout.
wrapper_classstr"inline-block"Tailwind classes applied to the inner SVG wrapper div.

Data format

python

data = [
    {"date": "2025-01-01", "value": 3},
    {"date": "2025-06-15", "value": 12},
]

Dates must be strings in YYYY-MM-DD format. Values must be non-negative integers. Dates outside the start_date/end_date range are ignored. Dates within the range that are missing from data render as empty cells using var(--secondary).

Color Modes

Discrete

Values are bucketed into len(color_scale) levels using a linear scale from 0 to max(value). The default scale is a 5-level GitHub-style green:

python

color_scale = [
    "var(--secondary)",  # 0  — empty
    "#9be9a8",           # 1  — low
    "#40c463",           # 2
    "#30a14e",           # 3
    "#216e39",           # 4  — high
]

Pass any number of hex strings to customize. More levels give finer granularity.

Interpolate

Colors are blended smoothly between min_color and max_color using the chosen interpolation function. Zero-value cells always use var(--secondary) regardless of min_color.

InterpolationBest for
"linear"Evenly distributed values
"sqrt"Sparse data where low values need visibility
"log"Data with extreme outliers that would wash out lower values