mokkiebear40k downloadsVisualize your activity and track goals, progress, habits, tasks, exercise, finances, and more—all in a single, interactive heatmap!
The Heatmap Tracker plugin for Obsidian is a powerful and customizable tool designed to help you track, visualize, and analyze data over a calendar year. Perfect for habit tracking, project management, personal development, or any kind of data visualization, this plugin enables you to create beautiful, interactive heatmaps directly within Obsidian. Whether you’re monitoring progress, visualizing trends, or staying on top of daily goals, the Heatmap Tracker enhances your productivity and organization. Discover its intuitive features, flexible customization options, and seamless integration with Obsidian in the detailed guide below.
Tip: Check Example Vault. There're lots of good examples (and I update it often).
This plugin requires the Obsidian Dataview plugin to be installed and enabled to automatically fetch data from your notes.
YYYY-MM-DD.md) that you want to track.photo-taking: 10photo-taking: true (counts as 1)Insert Heatmap Tracker to generate a heatmap through the interactive modal.This plugin comes with frontmatter tracking out of the box. You can use the heatmap-tracker codeblock with the following parameters:
```heatmap-tracker
property: <frontmatter_property_key>
```
This will look for frontmatter_property_key in your daily notes and activate a spot on the heatmap wherever that property is set.
You can also use an array of property names as such:
```heatmap-tracker
property: [<frontmatter_property_key_1>, <frontmatter_property_key_2>, ...]
```
This will aggregate the values of all specified properties on the heatmap.
You can narrow down which notes are included with path, tags, and filters — all optional, and all can be combined:
```heatmap-tracker
property: exercise
path: "daily notes"
tags: [journal]
filters:
- property: status
operator: equals
value: done
```
path: folder to search in. Leave unset to search the whole vault.tags: only include notes with at least one of these tags (the leading # is optional).filters: additional frontmatter conditions a note must satisfy — all conditions must match. Each entry has:property: the frontmatter key to check.operator: equals, contains, or notEmpty.value: compared against property's value (ignored, and not required, for notEmpty).You can add a heatmap tracker using command: Insert Heatmap Tracker. This is the easiest way to get started.
If you want something more involved, you may use a dataviewjs codeblock as such (update trackerData with your own dataset to visualize custom data points):
// Update this object
const trackerData = {
entries: [],
separateMonths: true,
heatmapTitle: "This is the title for your heatmap",
heatmapSubtitle: "This is the subtitle for your heatmap. You can use it as a description.",
}
// Path to the folder with notes
const PATH_TO_YOUR_FOLDER = "daily notes preview/notes";
// Name of the parameter you want to see on this heatmap
const PARAMETER_NAME = 'steps';
// You need dataviewjs plugin to get information from your pages
for(let page of dv.pages(`"${PATH_TO_YOUR_FOLDER}"`).where((p) => p[PARAMETER_NAME])){
trackerData.entries.push({
date: page.file.name,
// Use absolute file path so clicks open the exact note (for cases when you have multiple notes with the same name)
filePath: page.file.path,
intensity: page[PARAMETER_NAME],
});
}
// Optional: set base path so new files are created here if missing
trackerData.basePath = PATH_TO_YOUR_FOLDER;
renderHeatmapTracker(this.container, trackerData);
Notes
filePath for each entry (page.file.path), clicking a heatmap box opens that exact file. If the file is missing, the plugin offers to create it at the same path.filePath is not set on a box but trackerData.basePath is provided, the plugin proposes creating/opening trackerData.basePath/YYYY-MM-DD.md.This section is the authoritative reference for every trackerData parameter. EXAMPLE_VAULT has copy-pasteable dataviewjs examples for each one — each parameter below links to its example.
yearnumbernew Date().getFullYear())heatmapTitlestring | numberundefinedheatmapSubtitlestring | numberundefinedcolorSchemeobject{
"paletteName": "default",
"customColors": []
}
paletteName to reference a palette from plugin settings, or customColors to provide your own array of colors inline.customColorstringundefinedentries, not on trackerData itself). Sets the color for that specific entry, overriding colorScheme.entriesarray[
{ "date": "1900-01-01", "customColor": "#7bc96f", "intensity": 5, "content": "" }
]
date: The date of the entry (ISO string format).intensity: The data intensity for that date.content: Optional tooltip or note associated with the date.customColor: Overrides the color for that entry.filePath: Absolute path to the file to open when clicked.customHref: Custom URL to open when clicked (takes precedence over filePath).showCurrentDayBorderbooleantrueintensityConfigobject{
"scaleStart": undefined,
"scaleEnd": undefined,
"defaultIntensity": 4,
"showOutOfRange": true,
"excludeFalsy": undefined
}
scaleStart / scaleEnd: The minimum/maximum values of the intensity scale. Useful for a custom range, e.g. tracking reading time only between 30 minutes and 2 hours.defaultIntensity: Intensity assigned to entries that don't specify one.showOutOfRange: Whether entries outside scaleStart/scaleEnd are still shown (clamped) or hidden.excludeFalsy: When true, entries with falsy intensity (0, undefined, null, false) are excluded from the heatmap and don't break streaks.Migrating from
defaultEntryIntensity/intensityScaleStart/intensityScaleEnd: these top-level parameters are removed as of the API described here. Old codeblocks using them keep working (they're folded intointensityConfigautomatically), but new heatmaps should useintensityConfigdirectly.
basePathstringundefinedseparateMonthsbooleantruedisableFileCreationbooleanfalsetrue, clicking an empty heatmap box will not offer to create a new file.insightsarray[]Statistics.layout"default" | "monthly""default""default" renders the traditional GitHub-style week-column grid. "monthly" renders one row per month with days 1–31 as columns, providing a compact calendar-style view.monthsToShow, daysToShow, startDate/endDateThese four parameters all narrow which dates the heatmap displays instead of the full year. Only one wins when several are set — they're resolved in this order (highest priority first):
monthsToShow (number, default undefined) — current month plus the N previous months. monthsToShow: 3 displays 4 rows (current month + 3 prior). Best used with layout: "monthly".daysToShow (number, default undefined) — the last N days ending today.startDate + endDate (string, format YYYY-MM-DD, default undefined) — an explicit range. Both must be set, and startDate must not be after endDate.If none of these are set, the heatmap falls back to showing the full year. This precedence is implemented once, in resolveDateRange — that function's doc comment is the source of truth if this section and the code ever disagree.
Every heatmap has an Export tab alongside Heatmap Tracker / Statistics / Legend / Documentation. It turns your tracked data and daily notes into a single shareable report — a status update, a work log for a manager, an end-of-year summary — sitting between a bare calendar (too little context) and a folder of raw notes (too much noise).
-), grouped under "Week of ..." and per-day headings. The reader gets everything that actually happened, day by day, in one document instead of clicking through each note individually.Workday: 22 · Leave: 1 · Rest day but worked: 1 with a total (e.g. Total hours: 169) — or hide the breakdown, the total, or all values entirely.All of these preferences persist across sessions, so you only need to set them up once.
To be used with Obsidian Dataview, but could be used standalone or with other plugins as well (if you know some javascript).
Easily switch between years using left and right navigation arrows, allowing you to explore data across multiple years effortlessly.
You have lots of options for defining colors:
Customize insights such as:
Check this file for more information Insights
See src/localization/locales for the current list, and docs/add-new-language.md if you'd like to contribute a translation.
Additionally, you can use HTML to further customize the plugin's appearance.
Set layout: "monthly" to switch from the default GitHub-style grid to a calendar-style view with days 1–31 as columns. Combine with monthsToShow to display only recent months.
The Export tab renders a grid that matches your heatmap exactly (columns or rows, with automatic day-level month-splitting and band-wrapping for long ranges), then aggregates each day's own note content underneath it, grouped by week — plus a customizable legend, summary line, and date-range presets. See Exporting a Report above for details.
📍 Check out the Roadmap to see what's planned for the future!
📐 New to the codebase? ARCHITECTURE.md maps how data flows from a codeblock/dataviewjs script through to the rendered heatmap.
npm run dev - will start an automatic TS to JS transpiler and automatically copy the generated JS/CSS/manifest files to the example vault when modified (Remember to run npm install first).
After the files have been transpiled, the hot-reload plugin (https://github.com/pjeby/hot-reload) then reloads Obsidian automatically.
Hot-reload is installed in the example vault by default. its used to avoid restarting obsidian after every change to code.
(remember to add an empty .hotreload file to "EXAMPLE_VAULT/.obsidian/plugins/heatmap-tracker/" if not already present, as this tells hot-reload to watch for changes)
npm run build generates the files ready for distribution.
Tip: ctrl-shift-i opens the devtools inside Obsidian.