My Obsidian Daily Notes Template, 17 Months Later
Version 1.2 of my Obsidian Daily Notes Templater template: empty-day detection that ignores generated output, nested folder paths, configuration validation, and more reliable Markdown heading matching.
Seventeen months ago I published an Obsidian Daily Notes template that solved one small problem: every new daily note searches backward through the vault and embeds the most recent # Work section with actual content.
It skips weekends, holidays, missing notes, and days where I created a note but left the Work section empty.
The original version worked until I started using it with nested folders, custom filenames, Windows line endings, less predictable Markdown headings, and notes containing the template’s own generated output.
Version 1.2.0 keeps the same workflow while fixing those assumptions. A generated note now looks like this:
# Today's Notes
# Previous Work
![[Daily Notes/2026-08-03-Monday#Work]]
# Work
The template counted its own output as work
In the original version, the generated embed landed inside the top-level # Work section:
# Work
Previous Work Items (Monday)
![[2026-08-03-Monday#Work]]
When the template inspected that note later, it found content under # Work and treated the day as populated. The lookup could not distinguish between something I had written and the history it had generated itself.
As a result, a day with no new work could still be selected as the most recent populated note.
Version 1.2 keeps generated history under # Previous Work and reserves # Work for content you type yourself. The lookup only reads the Work section.
Folder paths
The original built file paths manually:
const filePath = `${fileName}.md`;
That only works when daily notes live in the vault root.
Version 1.2 normalizes the configured folder before building the path. You can use an empty string for the root, a regular folder, a nested folder, or a path with a trailing slash:
const DAILY_NOTES_FOLDER = "";
const DAILY_NOTES_FOLDER = "Daily Notes";
const DAILY_NOTES_FOLDER = "Journal/2026";
const DAILY_NOTES_FOLDER = "Daily Notes/";
The generated embed now includes the complete vault-relative path:
![[Journal/2026/2026-08-03-Monday#Work]]
This also removes ambiguity when two folders contain notes with the same filename.
Vault APIs instead of the adapter
The first version used the global app object and read files through the vault adapter:
const content = await app.vault.adapter.read(filePath);
The updated version uses the Obsidian instance provided by Templater:
const file = tp.app.vault.getAbstractFileByPath(filePath);
if (file && file.extension === "md") {
const content = await tp.app.vault.cachedRead(file);
}
The lookup now stays inside Obsidian’s normal Vault APIs. It also avoids checking whether a path exists and then performing a separate adapter read.
The change is not visible inside a daily note, but file lookup is more predictable and all paths remain vault-relative.
Configuration errors now produce warnings
Five constants sit at the top of the template:
const SECTION_NAME = "Work";
const DAILY_NOTES_FOLDER = "";
const FILENAME_FORMAT = "YYYY-MM-DD-dddd";
const DISPLAY_DATE_FORMAT = "YYYY-MM-DD";
const MAX_DAYS_TO_LOOK = 14;
The original version assumed all five were valid.
Version 1.2 checks that:
SECTION_NAMEis a non-empty stringFILENAME_FORMATis a non-empty stringDAILY_NOTES_FOLDERis a supported string valueMAX_DAYS_TO_LOOKis a positive integer- the current note title matches
FILENAME_FORMAT
Filename parsing is strict. With the format above, 2026-08-04-Tuesday is valid. These are not:
2026-08-04
Tuesday-2026-08-04
2026-08-04-Tuesdaay
Previously, an invalid filename produced an invalid date and sent the template looking for paths that could not exist. It now stops and renders a warning inside the note:
> [!warning] Lookup skipped — configuration error
> - Cannot parse date from "2026-08-04" using FILENAME_FORMAT "YYYY-MM-DD-dddd"
The lookback value is validated as well. Starting with the day before the current note, the template checks up to MAX_DAYS_TO_LOOK previous calendar days. A note on the final day of that range is included.
When no populated note is found, the output includes the range that was searched:
Previous Work Items (none found in last 14 days, back to 2026-07-21)
Increase the value if you regularly take longer breaks, but keep it bounded so a configuration mistake does not scan the entire vault.
Heading matching
The original lookup expected a heading written exactly as # Work.
Valid Markdown can also contain:
# Work
# Work
# Work #
Custom section names created another problem. Names such as Work (Team A) and C++ contain regular-expression metacharacters, so inserting them directly into the matcher caused it to fail.
Version 1.2 escapes the configured section name first:
const escapedSection = SECTION_NAME.replace(
/[.*+?^${}()|[\]\\]/g,
"\\$&"
);
The matcher now supports:
- LF and CRLF line endings
- spaces or tabs after
# - optional trailing hashes
- sections at the beginning or end of a file
- custom section names containing regular-expression characters
Only the next top-level heading closes the section.
In this example, ## Tickets and ## Notes both belong to # Work. The section ends at # Personal:
# Work
## Tickets
- DEV-123
- DEV-456
## Notes
Waiting for the API review.
# Personal
What counts as empty
A section containing only whitespace is empty.
A # Work section also counts as empty when the note’s only meaningful content appears under # Previous Work. Generated history is stored separately and ignored by the lookup.
Both cases are skipped, and the search continues backward. Subheadings are less clear-cut. A bare ## Tickets heading is still Markdown content, so a Work section containing only that heading may count as populated.
My rule is to add a subheading only when something goes beneath it.
Tests
The repository now includes a regression suite using Node’s built-in test runner:
node --test test/template.test.js
It covers:
- populated sections
- whitespace-only sections
- generated Previous Work content
- nested headings
- LF and CRLF line endings
- custom section names such as
Work (Team A)andC++ - root-level and nested folder paths
- trailing-slash normalization
- invalid configuration values
The test suite has no external dependencies. The Obsidian template remains a standalone Markdown file you can copy into your vault, while the tests live separately and verify the lookup logic.
Upgrading from the original version
Existing daily notes remain compatible, but you should replace the template file itself:
- Open your current template and save your configuration values.
- Copy the latest
template.md. - Reapply your folder, filename, section, and lookback configuration.
- Make sure generated output appears under
# Previous Work. - Keep your own notes beneath
# Work. - Create a test note and verify the generated embed.
Test at least these three situations:
- a previous day with real Work content
- a previous day with an empty Work section
- a missing note between two populated notes
It is also worth checking the first Monday after installation, since the weekend gap exercises the date lookup across multiple missing days.
What changed in practice
The workflow is still the same. I create a daily note, the template finds my most recent work, and I open the note during standup instead of reconstructing the previous few days from several files.
Version 1.2 changes how reliably that lookup works:
- empty days are skipped correctly
- generated history does not count as new work
- nested folders produce complete embed paths
- invalid filenames produce visible warnings
- custom headings and Windows line endings are supported
- the lookup behavior has regression coverage
Most of the update went into path handling, validation, Markdown variations, and error reporting. The original lookup idea did not need to change.
What’s next
The scope will probably stay narrow. The template finds one configured section from the most recent populated daily note and embeds it into today’s note. It could be extended to track multiple sections, connect to a task system, or generate weekly summaries, but those are different workflows.
For now, I would rather keep this one focused on preparing for standup. Latest version, documentation, tests, and changelog: Obsidian Smart Daily Notes Template.
The original article explains the reasoning behind the workflow.