Search...Search plugins and themes...
⌘K
Sign in
  • Get started
  • Download
  • Pricing
  • Enterprise
  • Account
  • Obsidian
  • Overview
  • Sync
  • Publish
  • Canvas
  • Mobile
  • Web Clipper
  • CLI
  • Learn
  • Help
  • Developers
  • Changelog
  • About
  • Roadmap
  • Blog
  • Resources
  • System status
  • License overview
  • Terms of service
  • Privacy policy
  • Security
  • Community
  • Plugins
  • Themes
  • Discord
  • Forum / 中文论坛
  • Merch store
  • Brand guidelines
Follow us
DiscordTwitterBlueskyThreadsMastodonYouTubeGitHub
© 2026 Obsidian

Habit Calendar

hedonihilisthedonihilist11k downloads

Monthly Habit Calendar for DataviewJS. Render a calendar inside DataviewJS code block, showing your habit status within a month.

Add to Obsidian
  • Overview
  • Scorecard
  • Updates11

中文文档

Monthly Habit Calendar for DataviewJS.

This plugin helps you render a calendar inside DataviewJS code block, showing your habit status within a month. It's based on the Habit Track plugin by @duoani.

This plugin is intended to be used alongside DataviewJS. All you need to do is prepare the data and call renderHabitCalendar in a dataviewjs code block.

There are two ways to populate the calendar:

  1. Dataview Table
  2. manually collected data

change log

1.0.x -> 1.1.x

changed the renderHabitCalendar interface, from

renderHabitCalendar(this.container, {
  year: number
  month: number
  width: string
  filepath: string
  format: string
  entries: Entry[]
})

to

renderHabitCalendar(this.container, dv, {
  year: number  // required
  month: number // required
  data: any // required
  width: string
  format: string
  note_pattern: string
})

Calendar from Dataview Table

For it to work, prepare a Dataview Table with the first column as the file link and other columns as habits.

```dataview
table coding as "Coding|👨‍💻", swim as "Swimming|🏊"
from "diarys"
```

For example, with the above DQL you will get a table like this:

dvtable

To render the table as a calendar, pass the result of DQL to renderHabitCalendar in a dataviewjs block:

```dataviewjs
const table = await dv.query(`
table coding as "Coding|👨‍💻", swim as "Swimming|🏊"
from "diarys"
`)
console.log(table)
renderHabitCalendar(this.container, dv, {
    year: 2023,
    month: 2,
    data: table
})
```

The calendar should look like this:

calendar

Notice that you can customize the habit label 👨‍💻 in the calendar by setting the header to "aaabbbccc|label". The text after the last "|" will be used as the label.

not using YYYY-MM-DD ?

If you are not using the 'YYYY-MM-DD' naming pattern with your daily note, you can set the pattern while calling renderHabitCalendar, so that this plugin can associate the habits with correct daily note:

```dataviewjs
const table = await dv.query(`
table coding as "Coding|👨‍💻", swim as "Swimming|🏊"
from "日记"
`)
console.log(table)
renderHabitCalendar(this.container, dv, {
    year: 2023,
    month: 2,
    data: table,
  date_pattern: 'YYYY年MM月DD日'
})
```

Calendar from manually collected data

This plugin also accepts customized data, jump to the bottom for detailed usage.

Basic Usage

```dataviewjs
renderHabitCalendar(this.container, dv, {
  year: 2023,
  month: 1,
  data: [{
    date: '2023-01-01',
    content: '⭐'
  }, {
    date: '2023-01-03',
    content: '⭐'
  }]
})
```

The above code will be rendered like this:

simple

If your daily note is of YYYY-MM-DD format, the calendar will be associated with your daily note automatically. You can hover over the number or click the number to access the corresponding note.

hover

Fill Calendar with HTML

Want to fill the calendar with HTML? Here we go:

```dataviewjs
renderHabitCalendar(this.container, dv, {
  year: 2023,
  month: 1,
  format: 'html',   // set the format to html
  data: [{
    date: '2023-01-01',
    content: '<a href="https://www.google.com">Google</a>'
  }, {
    date: '2023-01-03',
    content: '⭐',
  }]
})
```

html

Note: don't forget to enable the HTML in the plugin settings.

Fill Calendar with Markdown

If you don't want to write html, write markdown then.

```dataviewjs
renderHabitCalendar(this.container, dv, {
  year: 2023,
  month: 1,
  format: 'markdown',   // don't forget to change the format~
  data: [{
    date: '2023-01-01',
    content: '[Google](https://www.google.com)'
  }, {
    date: '2023-01-03',
    content: '⭐',
  }]
})
```

markdown

Note1: Sometimes the markdown text is not rendered correctly. Try switching to other files and switching back.

Customize link

In case you want your habit linked to other notes rather than associate with the daily note, you can pass in the link of each entry.

Say you want the first day linked to a note Monthly Target.md set the link attribute to it:

```dataviewjs
renderHabitCalendar(this.container, dv, {
  year: 2023,
  month: 1,
  data: [{
    date: '2023-01-01',
    content: '⭐',
    link: 'Monthly Target'  // like this line
  }, {
    date: '2023-01-03',
    content: '⭐',
  }]
})
```

Detailed Usage

The first argument should be the html container in which the calendar will be created. Most of the time, this.container will do.

The second argument should be the Dataview object dv, which will be used to get information of the notes.

You can pass the habit data through the third argument. The following fields are supported:

  • year: year of the calendar, apparently
  • month: month of the calendar
  • data: this filed can be a Dataview Table or a list of entries containing the habit data per day. A entry contains
    • date: date of the habit
    • content: whatever you want to put in the calendar
    • link: the file you want the entry to link to, just pass in the text inside [[]]. For example, if the original obsidian link is [[2023-01-01]], pass in 2023-01-01.
  • format: the way you want data[i].content to be rendered. Choose html or markdown to render as html or markdown, make sure their cooresopnding settings are enabled in the settings tab. Leave empty to treat the content as plain text.

How I record my habits

Check out the example vault. Your habits can look like this

Example

Add habit templates

In your diary template, add some habits you'd like to track:

```
## habits

- [ ] #habit read for (reading:: 30) minutes
- [ ] #habit jog for (jogging:: 30) minutes
- [ ] #habit get up before 8:00 am (wakey:: true)
```

Here we use #habit tag to distinguish habits from normal tasks and use Dataview attributes to record the intensity of the habit.

Record habit

Once you completed a habit, check the corresponding habit in your diary.

check habit

View your habits

Use dataviewjs to query the accomplished habits and pass the data to renderHabitCalendar. The following code will query the days you did some reading.

```
let files = dv.pages(`"diarys"`)
const habit = 'reading'
const year = 2023
const month = 2
const habit_str = '📖 {habit} min'  // {habit} will be replaced with the value of corresponding habit.

let data = []
for (let file of files) {
    console.log(file)
    for (let task of file.file.tasks) {
        if (task.tags.contains('#habit') && task.checked && task[habit]) { // select only checked habits
            data.push({date: file.file.name, content: habit_str.replace('{habit}', task[habit])})
        }
    } 
}
console.log(data)
renderHabitCalendar(this.container, dv, {year, month, data}) 
```

reading

View all you habits

Use the following code to display all the habits in a single calendar.

```dataviewjs
let pages = dv.pages(`"diarys"`)
const year = 2023
const month = 2
const date_pattern = 'YYYY-MM-DD'
const habit_tag = '#habit'
const habits = {
    'reading': '📖 x {habit} min',  // this habit will be displayed like '📖 x 30 min'
    'jogging': '🏃 x {habit} min',
    'wakey': '🌞',
}

let data = {}
for (let page of pages) {
    let date = page.file.name
    data[date] = data[date] || ''
    for (let task of page.file.tasks.filter(task => task.tags.contains(habit_tag) && task.checked)) {
        for (let habit in habits) {
            if (task[habit]) {
                data[date] += habits[habit].replace('{habit}', task[habit]) + '\n'
            }
        }
    } 
}

let calendarData = []
for (let date in data) {
    calendarData.push({date: date, content: data[date]})
}
renderHabitCalendar(this.container, dv, {year, month, data: calendarData, date_pattern}) 
```

It will look like this:

all habits

Plans

  • jump right to the diary on click
  • preview diary on hovering
  • support render markdown in calendar
58%
HealthFair
ReviewCaution
About
Render a monthly habit calendar inside a DataviewJS code block to visualize daily habit status for any month. Use Dataview tables or manual data entries as sources, map notes with custom date patterns, and set habit labels via header pipe notation.
CalendarDatesData
Details
Current version
1.2.0
Last updated
3 years ago
Created
3 years ago
Updates
11 releases
Downloads
11k
Compatible with
Obsidian 0.9.12+
Platforms
Desktop, Mobile
License
MIT
Report bugRequest featureReport plugin
Author
hedonihilisthedonihilist
github.com/hedonihilist
GitHubhedonihilist
  1. Community
  2. Plugins
  3. Calendar
  4. Habit Calendar

Related plugins

Day Planner

Day planning from a task list in a Markdown note with enhanced time block functionality.

Tracker

Track occurrences and numbers in your notes.

Periodic Notes

Manage your daily, weekly, and monthly notes.

Full Calendar Remastered

Complete Calendar HUB experience. Work with all your calendars in one place. Analyze your time and take action!

Mood Tracker

Track your moods & emotions easily. Visualize tracked history and browse the past entries.

Calendarium

Craft mind-bending fantasy and sci-fi calendars.

Journals

Manage your journals.

Chronology

A calendar and a timeline of the note's creation and modification.

Calendar

Explore your daily notes.

Prisma Calendar

Prisma turns any note with a date into a flexible planning system inside Obsidian. There are no rigid schemas or predefined structures — just your data, your rules, fully under your control.