Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCountry SelectCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputEmoji PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone SelectTree Select
Forms
Address InputBooking WidgetCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage GalleryImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadChat WidgetConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeTerminalText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowOrder History PagePost Listing WizardPricing PageProfile PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAgency PageAI Product PageAppointment Booking PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageHome Services PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePortfolio PagePress PageProduct Tour PagePromo PageReal Estate PageReferral PageResearch Report PageRestaurant PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageStorefront PageSwitch PageTemplate Gallery PageTravel PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

Charts & Stats

Heatmap calendar

Charts & Stats

Heatmap calendar React chart component for Tailwind CSS v4

GitHub ActivityLeaderboard
Live props
Content
endDate
today
valueFormat
currency
decimalsAuto
locale
label
description
Layout
weeks53
weekStartsOn-1
overflow
showMonthLabels
showWeekdayLabels
showLegend
showTotal
Appearance
levels5
tone
color
emptyColor
cellSize
radius
surface
Behavior
showTooltip
tooltipMode
animated
State
defaultValue
loading
Code
import { HeatmapCalendar } from "@/components/ui/heatmap-calendar"

<HeatmapCalendar />
  1. Home
  2. Components
  3. Charts & Stats
  4. Heatmap calendar
Pro

Heatmap calendar React chart component for Tailwind CSS v4

v1.0.0·Added in 1.0.0

A React heatmap calendar of activity by day over weeks or a year, on a one-hue scale with month and weekday labels, a total and a legend.

The first time the heatmap scrolls into view its week columns fade and rise from left to right in one quick sweep. Hover a day for a readout of its date and value (the nearest day answers, so gaps never flicker) and click it to select it (click the selected day again to clear it). On a phone the year is a horizontal rail that opens at the newest week, with fading edges and week snapping while the weekday labels stay pinned; vertical drags still scroll the page. A first tap on a day pins its readout above the grid, a second tap on the same day selects it, a tap outside clears the readout. With the keyboard, Tab lands on one day (the selected one, else the newest): Left / Right move by a week, Up / Down by a day, Home / End jump to the first and last day shown, PageUp / PageDown move by four weeks, Enter or Space select (again to clear) and Escape clears the selection. Every day has a spoken label and a screen reader table lists each month's total. When the data changes the colors crossfade.

Heatmap calendar is a Pro React chart component in the React chart and stat components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add heatmap-calendar, together with Chart kit, Date utils, useControllableState, useScrollEdges, useLocale and 1 more.

Add to a project

Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup

npx wingo-ui@latest add heatmap-calendar

Heatmap calendar is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans

Usage

Import HeatmapCalendar from @/components/ui/heatmap-calendar.

import { HeatmapCalendar, type HeatmapDatum } from "@/components/ui/heatmap-calendar"

const data: HeatmapDatum[] = [
  { date: "2026-09-21", value: 12 },
  { date: "2026-09-22", value: 9 },
  { date: "2026-09-23", value: 15 },
  { date: "2026-09-24", value: 7 },
]

export function TeamActivity() {
  return (
    <HeatmapCalendar
      data={data}
      endDate="2026-09-24"
      locale="en-US"
      label="Northwind CRM team activity"
      labels={{
        total: (n) => `${n} activities in the last year`,
        cell: (value, date) => `${value} activities on ${date}`,
        empty: (date) => `No activity on ${date}`,
      }}
      onDateClick={(date) => console.log(date)}
    />
  )
}

Props

  • data*
    HeatmapDatum[] = { date: string; value: number; label?: string }[]

    One entry per iso day ("2026-09-24"). Missing days count as 0, duplicate dates add up, days outside the window are ignored and negative values draw as 0. label names the value in the tooltip.

  • endDatedefault ""
    string

    Iso day of the last column. Empty uses the newest date in data (never the clock, so the server and the browser agree).

  • todaydefault ""
    string

    Iso day that gets the today ring. Empty uses the viewer's date, read after hydration.

  • weeksdefault 53
    number

    Week columns in the window, ending with the week of endDate.

  • weekStartsOndefault -1
    0 | 1 | 2 | 3 | 4 | 5 | 6 | -1

    First row of each column: 0 is Sunday, 1 Monday ... 6 Saturday; -1 follows the locale (Monday in en-GB, Sunday in en-US). Changes the row order and the weekday labels.

  • levelsdefault 5
    number

    Color steps including the empty level. Ignored when thresholds are given.

  • thresholds
    number[]

    Lower bound of each level above 0, ascending: [1, 5, 10, 20] gives five levels. Default: quantiles of the non-zero values, so any activity is at least level 1.

  • tonedefault "neutral"
    "neutral" | "brand" | "success" | "warning" | "danger" | "info"

    The hue of the scale. Level 0 is the foreground at 7% (see emptyColor), then the tone from 30% to 100% in equal steps. neutral is the foreground ink.

  • colordefault ""
    string

    Any CSS color, a palette slot ("series-3") or a tone name. Replaces the tone.

  • emptyColordefault ""
    string

    Fill of the days without activity (level 0 and its legend swatch): any CSS color or a palette slot. Empty uses the foreground at 7%, visible on white and on the dark surface.

  • cellSizedefault "md"
    "sm" | "md" | "lg"

    10 / 12 / 16px cells with 2 / 3 / 4px gaps.

  • radiusdefault "sm"
    "none" | "sm" | "md" | "full"

    Cell corners: 0 / 2 / 3px or circles. The legend swatches follow.

  • overflowdefault "scroll"
    "scroll" | "fit"

    scroll: a horizontal rail that opens at the newest week, with edge fades and week snapping. fit: only the newest weeks that fit the width, nothing scrolls.

  • showMonthLabelsdefault true
    boolean

    Short month names above the first column of each month, skipped when two would collide.

  • showWeekdayLabelsdefault true
    boolean

    Monday, Wednesday and Friday in the locale's two-letter form, pinned while the rail scrolls.

  • showLegenddefault true
    boolean

    The Less ... More scale under the grid.

  • showTotaldefault true
    boolean

    The header sentence with the window's total (labels.total).

  • showTooltipdefault true
    boolean

    The readout on hover, tap and keyboard focus. Cell labels and the table still carry every value when off.

  • tooltipModedefault "auto"
    "auto" | "float" | "fixed"

    float rides beside the day, fixed pins above the grid (never under a finger); auto is fixed on touch screens.

  • value
    string | null

    The selected day (iso) when you control it; it gets a 2px ring. Pair with onValueChange.

  • defaultValuedefault "null"
    string | null

    The day selected at first (YYYY-MM-DD), uncontrolled; null selects none. The playground offers a few sample days.

  • onValueChange
    (date: string | null) => void

    Called when a day is selected, or with null when the selection clears.

  • onDateClick
    (date: string, datum: HeatmapDatum | null) => void

    A click, a second tap or Enter on a day; datum is null for days without data.

  • valueFormatdefault "number"
    ChartValueFormat = "number" | "compact" | "currency" | "percent"

    How values print in the tooltip, the total, the cell labels and the table.

  • currencydefault "USD"
    string

    ISO 4217 code for valueFormat currency ("$12,480").

  • decimalsdefault 0
    number

    Fraction digits for values in the tooltip, the total and the table; auto by magnitude when unset (Auto in the playground).

  • formatValue
    (value: number, context: { kind: "tooltip" | "label" | "table" | "total"; date?: string }) => string

    Replaces the built-in value formatting everywhere.

  • localedefault ""
    string

    Locale for numbers, month and weekday names, dates and the first day of the week. Empty uses the UIProvider / LocaleProvider locale.

  • animateddefault true
    boolean

    The column sweep on first view and the color crossfade on data changes. Reduced motion always shows the final state.

  • loadingdefault false
    boolean

    A skeleton in the grid's box and in the total.

  • emptyState
    ReactNode

    Replaces the grid when data is empty. Without it an empty heatmap shows the zero grid and labels.noData.

  • labeldefault ""
    string

    Accessible name of the figure and the grid, caption of the table. Empty uses labels.table.

  • descriptiondefault ""
    string

    A one-sentence takeaway for screen readers.

  • surfacedefault ""
    string

    The color behind the heatmap, painted under the pinned weekday labels and the today ring. Empty uses the surface token; set it when the heatmap sits on another background.

  • actions
    ReactNode

    Trailing content of the header row: a period switch, a link.

  • labels
    Partial<HeatmapCalendarLabels>

    Strings: less, more, total(count, weeks), cell(value, date, datum), empty(date), noData, table, month, valueName, activeDays, loading. English by default.

  • classNames
    Partial<Record<HeatmapCalendarSlot, string>>

    Classes for header, total, actions, scroller, grid, cell, month, weekday, legend, tooltip and table.

Parts

  • DEFAULT_HEATMAP_CALENDAR_LABELS

    constant

    The English strings: Less and More, the total line, the cell and empty-day names, the no-data and loading text and the screen reader table's headers.

  • getHeatLevel

    function

    getHeatLevel(value, thresholds) returns a cell's level: 0 for nothing, then one step per ascending threshold the value reaches. Use it to color a matching legend or list.

  • HeatmapDatum

    type

    One day of data: { date, value, label? } with the date as YYYY-MM-DD; label names what the value counts in the tooltip.

Examples

GitHub look

tone success, radius sm, cellSize sm.

A quarter, large cells

weeks 13, cellSize lg, brand tone.

Fit vs scroll on a phone

Two 360px frames: the rail and the fitted grid.

Custom thresholds

Fixed bands [1, 5, 10, 20] instead of quantiles.

Selection drives a list

The selected day filters the activity log below.

Loading and empty

The skeleton, and the zero grid when there is no data.

Monday start, circles

en-GB week, a palette slot color, circles and formatValue.

Dependencies

chart-kitdate-utilsuse-controllable-stateuse-scroll-edgesuse-localeui-config

Files

  • components/ui/heatmap-calendar.tsx

FAQ

Is the React Heatmap calendar chart component free?

The preview, props and docs are free; copying the source needs a Pro plan. See plans.

How do I install Heatmap calendar?

Run npx wingo-ui@latest add heatmap-calendar. It also adds the Wingo UI items it builds on (Chart kit, Date utils, useControllableState, useScrollEdges, useLocale and 1 more). With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/heatmap-calendar.

How does Heatmap calendar behave on phones?

On a phone the year is a horizontal rail that opens at the newest week, with fading edges and week snapping while the weekday labels stay pinned; vertical drags still scroll the page.

See also

GitHub ActivityCalendarChart kitlib

Used in

Impact PageYear in Review Page

More React chart and stat components

Stat CardSparklineLeaderboardArea chartBar chartDonut chart
All 16 React chart and stat components

Articles using this component

  • React GitHub Contribution Graph: Data, Themes and A11y

    Component guides · Oct 19, 2026 · 9 min read

PreviousGitHub ActivityNext Leaderboard

Northwind CRM team activity

Calls, emails and meetings per day, 53 weeks to Sep 24, 2026

2,824 activities in the last year

LessMore
Northwind CRM team activity
MonthActivitiesActive days
September 20257710
October 202529028
November 202520727
December 202530430
January 202620126
February 202620025
March 202626229
April 202628026
May 202622629
June 202627528
July 202622829
August 202610525
September 202616922
Pick a day for details: click it, or tap twice on a phone.

endDate: auto · value: null · tooltip: auto · sweep: on