webdevpuneet.com

ECharts Calendar Heatmap — Free Contribution Graph Snippet

ECharts Calendar Heatmap · Charts · Plain HTML, CSS & JS · Live preview

CategoryCharts

Copy & paste

Source Code

Requires
<div class="ech-wrap">
  <div class="ech-card">
    <div class="ech-head">
      <div>
        <div class="ech-title">Contribution Activity — 2026</div>
        <div class="ech-sub">Darker cells mean more commits that day</div>
      </div>
      <div class="ech-total" id="echTotal">0 contributions</div>
    </div>
    <div class="ech-chart" id="echChart"></div>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Calendar coordinate system
Dates map to grid cells automatically — no manual layout.
GitHub-style 5-stop scale
visualMap drives color without rendering its own UI.
Deterministic sample data
Seeded generator, same pattern and total every load.
Realistic distribution
Weighted toward zero so most days are quiet, like real data.
Exact-count tooltip
Hover any cell for its date and contribution count.
Month and day labels
Axis chrome matches the familiar contribution-graph look.
Header total
Sums the full dataset for an at-a-glance yearly count.
Responsive canvas
ResizeObserver keeps the grid sized to its container.

About this UI Snippet

ECharts Calendar Heatmap — A Full Year of Daily Activity in One Grid

Screenshot of the ECharts Calendar Heatmap snippet rendered live

The GitHub contribution graph is one of the most recognizable data visualizations on the web — a full year compressed into a grid of colored squares, dense enough to spot streaks and gaps at a glance. Apache ECharts ships the exact building blocks for it: a calendar coordinate system that lays out a year as a week-by-day grid, and a heatmap series that colors each cell by value.

calendar is a coordinate system, not a chart type

Most ECharts series draw on a Cartesian or polar grid. heatmap here instead targets coordinateSystem: 'calendar', and the calendar component (declared as its own top-level option, like grid or polar) handles converting each date string into a cell position — you supply [date, value] pairs and never compute a row or column yourself.

visualMap drives the color scale, invisibly

The five-stop green scale (#ebedf0 through #196127, GitHub's own palette) comes from a visualMap component with show: false — it still does its job of mapping each cell's value to a color along that gradient, it just doesn't render its own legend/slider UI, which would be redundant for a calendar that's already self-explanatory.

Seeded data keeps the demo honest

The 365 daily counts come from a small seeded pseudo-random function rather than Math.random(), so the same pattern (and the same total in the header) renders on every page load — useful for anyone comparing before/after CSS tweaks, and standard practice for any demo that isn't hooked to live data.

The zero-weighted distribution matters

Real contribution graphs are mostly empty with occasional bursts, not evenly distributed. The generator weights toward zero (r > 0.55 before any commits count at all) specifically so the rendered heatmap looks like an actual usage pattern instead of uniform noise — a detail that matters more than it seems for a demo meant to be visually convincing.

Reusing it

Swap the generated data for real daily counts (from git log, an activity log table, or an analytics export keyed by date) and everything else — the color scale, month/day labels, tooltip — keeps working unchanged, since it only depends on [isoDateString, number] pairs.

Step by step

How to Use

  1. 1
    Add the ECharts CDNLoad echarts.min.js before the snippet's JS runs.
  2. 2
    Paste HTML, CSS, and JSA full year renders as a color-scaled grid of day cells.
  3. 3
    Read the header totalIt sums every day's count across the year.
  4. 4
    Hover a cellThe tooltip shows that exact date and its count.
  5. 5
    Scan for streaksDarker green runs show consistent activity.
  6. 6
    Scan for gapsLight gray cells mark days with zero activity.

Real-world uses

Common Use Cases

Developer profile pages
Show commit or pull request activity as a year of coloured squares, the way GitHub does, so streaks and gaps are visible at a glance.
Habit and streak trackers
Track any daily count, such as workouts, reading or meditation, and hover a square for the exact number on that day.
Content publishing dashboards
Visualise posting cadence across a year to spot quiet months, using a five-stop colour scale that visualMap drives without drawing its own UI.
Support and ops activity
Show ticket volume or deploy frequency per day, with the calendar coordinate system placing dates in grid cells automatically.
Personal analytics tools
Pair with an ECharts revenue line with zoom brush so the daily grid sits beside a trend view.
Related: ECharts Treemap Storage Breakdown
See the ECharts Treemap Storage Breakdown for a related charts pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

The heatmap series is set to coordinateSystem: 'calendar' instead of the default Cartesian grid, and a separate top-level calendar component (given a range like '2026') handles converting each [date, value] pair in the series data into the correct week-column and day-row cell position. You never compute a row or column index yourself — only supply ISO date strings and numbers.

visualMap normally renders a visible legend or slider that lets viewers interact with the color scale. Here it is used purely for its color-mapping logic — converting each cell's numeric value into a color along the five-stop green gradient — without rendering that UI, since a calendar heatmap is self-explanatory and an extra legend would be redundant clutter.

Math.random() produces a different pattern (and a different header total) on every page load, which makes the demo inconsistent for screenshots, visual regression checks, or anyone comparing two versions side by side. The seeded function derives its output from a fixed formula, so the exact same 365 values render every time the snippet runs.

Replace the generator loop with your own array of [isoDateString, count] pairs — from a database query grouped by day, a git log parse, or an analytics export. The calendar component, color scale, tooltip, and header total all read from that data array's shape and need no other changes.

Initialize the chart once against a container ref/template ref and call setOption whenever your date-keyed data changes — for a full year swap, that's just a new range value and a new data array. Dispose the instance on unmount, and keep the same ResizeObserver pattern since ECharts does not auto-resize with CSS layout changes alone.