Books per year
Dashy vault/Guides/Books per year
Guides/Books per year.md

Books per year: a reading log chart in Obsidian

Properties
blocks
statschart
keys
bucket: yeardate_field
needs dataview
checked on
Dashy 2.0.0

You keep a note per book and want to see how many you read each year, and how this year is going against the last. A book note is named after the book, not a date, so the date has to come from a property. Two Dashy blocks read it: a row of numbers for this year and a bar per year.

Three cards for this year: books read with the change against last year, pages read and the average rating; below them one bar per year for six years, a dashed goal line at 24, and this year's bar drawn lighter
What you have at the end of step 3. From a demo vault.
Obsidian Charts turned on? Write dashy-chart

Obsidian Charts also uses the chart code block. When Charts is on, Dashy leaves chart to it. Name the chart below dashy-chart instead: same keys, always Dashy.

1Give each book the day you finished it

One date property for when you finished the book. Here it is date_read, next to the page count and your rating.

Reading/Project Hail Mary.md
---
date_read: 2026-03-14
pages: 476
rating: 5
---

Read it in four evenings.
  • date_read: 2026-03-14Year, month, day, or a Date property from Obsidian's Properties panel. A book you are still reading leaves it out and is not counted yet.
  • pages, ratingOptional. Plain numbers, for the cards in step 2.

If your book notes are named with the date first, like 2026-03-14 Project Hail Mary.md, that works too: leave date_field out of the blocks below and the date is read from the name.

2This year in numbers

Make a note for the log, say Reading log.md, and paste this block. It is the row of cards in the picture above.

Reading log.md
```stats
source: Reading
date_field: date_read
period: year
items:
  - { label: Books this year, agg: count, compare: true, better: up, icon: 📚 }
  - { label: Pages this year, field: pages, agg: sum }
  - { label: Average rating, field: rating, agg: avg, precision: 1 }
```
  • source, date_field, periodWritten once, above items, they apply to every card. A card can still set its own, and its own wins.
  • period: year1 January to today. Books without a date_read are left out first.
  • compare: trueThe change against last year, to the same day: on 5 October it compares with 1 January to 5 October of last year, not with all of it. better: up makes a rise green.

3One bar per year

Paste this under the cards.

Reading log.md
```chart
source: Reading
date_field: date_read
agg: count
bucket: year
type: bar
range: 1825d
label: Books
goal: 24
```
  • agg: countCounts the books dated in each year. No field is needed.
  • bucket: yearOne bar per calendar year, labelled with the year alone.
  • range: 1825dFive years back from today. The window starts on 1 January of the year it reaches, so in 2026 the bars run from 2021 to 2026. Leave it out for the last ten years and this one.
  • goal: 24A dashed line at 24 books, its value named in the legend.

This year's bar is drawn lighter and its tooltip says "so far": the year is not over. A year with no books is a 0, since counting nothing is still a count. Hover a bar for the number.

The whole note

Both blocks in one piece, ready to paste into an empty note.

Reading log.md
```stats
source: Reading
date_field: date_read
period: year
items:
  - { label: Books this year, agg: count, compare: true, better: up, icon: 📚 }
  - { label: Pages this year, field: pages, agg: sum }
  - { label: Average rating, field: rating, agg: avg, precision: 1 }
```

```chart
source: Reading
date_field: date_read
agg: count
bucket: year
type: bar
range: 1825d
label: Books
goal: 24
```

Ask your agent

With Dashy's skill installed, an AI agent can write this from a request in plain words: it looks at your property names and folders first, then writes the blocks. Open the note in Reading view and paste any error a block shows back to the agent.

Prompt
In a new note called Reading log, show how many books I read this year against last year, how many pages that was and my average rating. Under it, a bar for each of the last five years with a line at my goal of 24 books. Each book note has the date I finished it.

What comes back is something like the note above. The agent's YAML may differ from the guide's, with your own property names and labels, but it should do the same, and this guide is the reference to compare it against. More on working with an agent.

The same chart with dataviewjs

The usual way: a Dataview script groups the books by year and hands the numbers to Obsidian Charts.

With Dataview, JavaScript queries on, and Obsidian Charts
```dataviewjs
const years = {};
for (const p of dv.pages('"Reading"').where(p => p.date_read)) {
  const y = String(p.date_read.year);
  years[y] = (years[y] ?? 0) + 1;
}
const labels = Object.keys(years).sort();
window.renderChart({
  type: "bar",
  data: { labels, datasets: [{ label: "Books", data: labels.map(y => years[y]) }] },
}, this.container);
```
  • Two plugins: Dataview with Enable JavaScript queries on, which is off by default, and Obsidian Charts for renderChart.
  • A year with no books has no key in years, so it drops off the axis instead of showing 0.
  • No goal line, and nothing marks the current year as unfinished. Both are more code.
  • This year's count, pages and the change against last year are a second script.
Dashydataviewjs
Group by yearbucket: yearThe loop above
A year with no books0Missing from the axis
Goal linegoal: 24Write it
This year against lastcompare: trueWrite it
What you installDashyDataview, JavaScript queries on, and Obsidian Charts

Variations

A yearly goal as a bar

How far along this year's goal you are, as a percentage.

progress
```progress
items:
  - { label: Reading goal, source: Reading, date_field: date_read, period: year, agg: count, goal: 24, icon: 📚 }
```

Pages, not books

A long book counts for more. sum adds the pages up; a year with none is a gap.

chart
```chart
source: Reading
date_field: date_read
field: pages
bucket: year
type: bar
label: Pages
```

This year by month

The same books, one bar per month since 1 January.

chart
```chart
source: Reading
date_field: date_read
agg: count
bucket: month
range: year
type: bar
label: Books
```

Only the five-star ones

where narrows the books first. It works on the chart and on the cards alike.

stats
```stats
source: Reading
items:
  - { label: Five-star books, where: "rating = 5", agg: count }
```

If a block shows an error

A block that cannot do what you asked says so, inside the note. The ones you are likely to see here:

  • None of the selected notes has a date in "date_read". The property is spelled differently in the book notes, or source names the wrong folder.
  • None of the selected notes has a name starting with a date like YYYY-MM-DD. The block has no date_field and the book notes are named after the book. Add date_field: date_read.
  • bucket expects day, week, month or year. Something like yearly was written. The chart falls back to days until you fix it.
  • An error from Obsidian Charts, not from Dashy. With Charts on, a chart block is Charts'. Write dashy-chart.