Ein Baum und seine Abenteuer



Daily Note navigation in Obsidian using Dataview


Daily notes in Obsidian are ephemeral, they matter most for the day there were written and maybe the days surrounding them. But, after a week, we hardly ever refer back to them. If something truly mattered, we probably pulled it out to another note.

That being said, I often find myself wanting to refer to yesterdays note. And Obsidian doesn't provide a native way to get to yesterdays note. My current solution to this is to add links to the last and next daily note into every daily note via a template.

You can read up on Obsidian Templates, if you are not familiar. This article will only cover how I am creating the links.

Jump to the final solution if you are only interested in copy-pasting.

DataviewJS and assumptions

This solution uses DataviewJS inline queries. Remember to only use JavaScript in Obsidian that you understand and trust, as it will have full access to your system!

All code introduced here needs to run inside pages that have a file.day attribute. Which holds true for daily notes that have their date in their name or a Date field.

Make sure to enable the DataviewJS feature from the Dataview settings!

Formatting dates

Dataview uses Luxon for date manipulation. Once we have a date object, e.g. from file.day, we can call .toFormat on it to get the date in the format we need. For our usecase, we need to format dates so that they fit the names of our daily notes.

file.day.toFormat('yyyy-MM-dd')

Why Dataview?

I decided to use Dataview for my links because this will keep my links up to date, especially for "next daily".

Alternatively you could also use Templater to link to the previous daily note. But it would only work for next daily, if you hardcode that to be the next day, as templates are evaluated at file creation.

Assumptions

This implementation assumes that:

  1. daily notes life inside a folder (mine is named journal)
  2. daily notes are named with only their date, following yyyy-MM-dd (e.g. 2023-10-14)

If any of these assumptions do not hold true for your Obsidian Vault, you will need to make adjustments to the code.

Previous Daily Note

I am using daily notes mainly for work which means that I often do not have daily notes on the weekend. The meaningful "previous note" for me is thus "the last daily note I took" and not always "yesterdays note".

The previous daily note is not always yesterdays note!

Initially I rendered out the link in a codeblock. This allowed me to use intermediate variables, while I was figuring out the right logic.

const date = dv.pages('"journal"')
  .where(page => page.file.day && page.file.day < dv.current().file.day)
  .sort(page => page.file.day, "desc").file.day[0]

const formattedDate = date.toFormat('yyyy-MM-dd')

dv.el("p", formattedDate)
  1. dv.pages let's us "query" for pages, in this case all pages inside the journal folder, where only my daily notes life. Remember that you need '""' around the folder name (see docs).
  2. .where can be used to further filter down the pages we get. Here we look for daily notes prior to the current note.
  3. .sort we sort the resulting list of pages by their day.
  4. .file.day effectively "maps" all entries in the list to their day.
  5. [0] gets the first element of the array of days.

We can now consense this down to a single line and add the prose to create an internal link to the previous daily note. By turing this inline, we can later add the forward link on the same line.

$= "[[" + dv.pages('"journal"').where(page => page.file.day && page.file.day < dv.current().file.day).sort(page => page.file.day, "desc").file.day[0].toFormat('yyyy-MM-dd') + "| Previous Daily]]"
  1. $= is the default prefix for inline DataviewJS.
  2. "[[" + +"| Previous Daily]]", wraps the output of our logic to get "the day of the previous note", thus creating a valid, internal Link to it.

This logic does not handle the previous daily note not existing! Consider creating an empty one if you are starting with a clean Vault.

Next daily

Similar to the previous daily note, the next daily note may not necessarily be tomorrows one. Additionally the next daily might not necessarily exist yet. To take all these cases into consideration, we need more logic. I opted to enable this through an Immediately Invoked Function.

(function () {
const date = dv.date(dv.pages('"journal"')
  .where(page => page.file.day && page.file.day > dv.current().file.day)
  .sort(page => page.file.day, "asc").file.day)[0]
  ?.toFormat('yyyy-MM-dd');

if (!!date) return "[[" + date + "| Next Daily ]]";
return "No next note";
})()
  1. (function() {})() is our immediate invoked function. Through this we can use the return value of more complex logic.
  2. date uses similar logic to above, only this time looking for the next daily note.
  3. .? the Optional Chaining Operator enables us to evaluate the entire expression to undefined, instead of braking on the .toFormat not existing on undefined or null.
  4. if(!!date) casts the date to a boolean and returns the link to it, if it exists.
  5. In case there is no next daily note yet, I opted to show a hint. You could also figure out the next date and display a link to create tomorrows note.
  6. ;, Semicolons are immensely important here! While JavaScript inserts them for you, we need them here to condense the code to a single line!

This time we do not need to add anything around our code, as it already returns the link or text we want to display. We only need to remove the linebreaks.

$= (function () {const date = dv.date(dv.pages('"journal"').where(page => page.file.day && page.file.day > dv.current().file.day).sort(page => page.file.day, "asc").file.day)[0]?.toFormat('yyyy-MM-dd');if (!!date) return "[[" + date + "| Next Daily ]]";return "No next note";})()

Putting it all together

Navigation between daily notes in Obsidian
Navigation between daily notes in Obsidian

For some nicer look-and-feel I added a bit of fluff around the links. Here is a copy and paste of the final solution:

<< `$= "[[" + dv.date(dv.pages('"journal"').where(page => page.file.day && page.file.day < dv.current().file.day).sort(page => page.file.day, "desc").file.day)[0].toFormat('yyyy-MM-dd') + "| Previous Daily]]"` | `$= (function () {const date = dv.date(dv.pages('"journal"').where(page => page.file.day && page.file.day > dv.current().file.day).sort(page => page.file.day, "asc").file.day)[0]?.toFormat('yyyy-MM-dd');if (!!date) return "[[" + date + "| Next Daily ]]";return "No next note";})()` >>
Compass Emoji - find your bearing
Compass Emoji - find your bearing

Author

Portrait picture of Hendrik

I am a JavaScript and GenAI Enthusiast; developer for the fun of it!
Here I write about webdev, technology, personal thoughts and anything I finds interesting.

More about me

Read next

Trying my hands at Deno v1.0

Denos awesome Dinosaur logo.
Denos awesome Dinosaur logo.

Following the release of Deno v1.0 I got excited to try my hands at it. These are my first experiences writing a simple tool in Deno.

A super fast introduction to Deno: Deno is the spiritual successor of Node trying to fix design mistakes that were made early on but recognized only late into the project. Deno supports TypeScript out of the box and relies on web-standards. In Deno you can import ES modules from any URL and use fetch like you would in the browser. To help unify the community on processes and workflows Deno provides a wide array of stdLibs and has build in solutions for bundling, testing and code formatting. You can read more in the Deno v1 release post.

Polling using RxJS

A real life stream in action.
A real life stream in action.

As observables are gaining more and more popularity in JavaScript we are looking to accomplish our everyday tasks using them and evaluating whether they are really worth all the hype. One task you might find yourself doing is polling the backend to know whether a longer running task has completed.

We will walk through an example of such a scenario and implement a solution using RxJS. On our way we will learn some basic operators for RxJS, and a few techniques as well as how to avoid a pitfall or two. At the end I will present a real-world example to show you how to implement what we learned in a specific scenario.

Holiday greetings with GenAI

Festive Greetings - ChatGPT and Midjourney
Festive Greetings - ChatGPT and Midjourney

Happy Holidays and festive greetings, powered by ChatGPT, Midjourney and a little bit of Photoshop.

Utilizing my Custom GPT for Midjourney prompts (open source on GitHub), I generated the image and some subtle variations in three rounds. Finally touching it up with a tagline in Photopea.