Allow {{property.<key>.<component>}} syntax so folder paths can be built
from date frontmatter (e.g. Archive/{{property.created.year}}) without
separate year/month fields. Supports year, month, day, iso, monthName, and
dayOfWeek with literal-key precedence and timezone-safe ISO parsing.
Closes #98
7.4 KiB
Destination templates
Instead of a fixed folder path, a rule's destination can include placeholders that are filled in from the note's own tags and frontmatter properties at move time. This lets one rule route notes to many different folders based on their content.
Syntax
Placeholders are wrapped in double braces: {{ … }}.
The inner text must start with tag. or property., followed by a key:
{{tag.<key>}}
{{property.<key>}}
Static path segments can appear before, between, or after placeholders:
Clients/{{property.client}}/Notes
Archive/{{property.year}}/{{property.project}}
Areas/{{tag.work}}
Valid examples
| Template | What it produces |
|---|---|
Projects/{{property.client}}/Notes |
Projects/Acme/Notes (if client: Acme) |
Areas/{{tag.work/personal}} |
Areas/work/personal (if tag #work/personal exists) |
Archive/{{property.year}} |
Archive/2025 (if year: 2025) |
Inbox/{{property.status}} |
Inbox/done (if status: done) |
Invalid examples (will fall back to raw string)
| Template | Problem |
|---|---|
Clients/{{property.client |
Unclosed {{ |
Clients/{{}} |
Empty placeholder |
Clients/{{property}} |
Missing key after prefix |
Clients/{{category.foo}} |
Unknown prefix (only tag and property allowed) |
{{property.<key>}}
Looks up <key> in the note's frontmatter.
| Frontmatter value | Resolves to |
|---|---|
| A string | The string as-is |
| A number or boolean | Converted to string |
| An array | Items joined with , (comma + space) |
| Missing, null, or undefined | Empty string |
If the placeholder resolves to an empty string, that segment of the path is missing. If the entire destination resolves to empty or whitespace, the note is not moved.
Date property components
For date frontmatter properties, append a component suffix to extract parts of the date at move time:
{{property.<key>.<component>}}
Supported components:
| Component | Example output (created: 2025-06-13) |
Notes |
|---|---|---|
year |
2025 |
4-digit year |
month |
06 |
Zero-padded month (01–12) |
day |
13 |
Zero-padded day (01–31) |
iso |
2025-06-13 |
Normalized ISO date (time ignored) |
monthName |
June |
English full month name |
dayOfWeek |
friday |
Lowercase English day name |
Examples
| Template | Frontmatter | Resolves to |
|---|---|---|
Archive/{{property.created.year}} |
created: 2025-06-13 |
Archive/2025 |
Journal/{{property.created.year}}/{{property.created.month}} |
created: 2025-06-13 |
Journal/2025/06 |
Days/{{property.created.dayOfWeek}} |
created: 2025-06-13 |
Days/friday |
Type {{property.<dateProperty>. in the destination field to get component suggestions for date properties discovered in your vault.
Literal property keys take precedence
If a frontmatter key exactly matches the full placeholder path (including the suffix), the raw property value is used instead of date extraction. For example, if you have a property literally named created.year, then {{property.created.year}} resolves to that property's value — not the year from created.
To gate moves on a date property, add a trigger: properties → created (date) → has any value.
{{tag.<key>}}
Looks for a tag that matches <key>, adding a # prefix if not present.
Resolution works in two steps:
- Exact match: if any tag on the note equals
#<key>exactly, the placeholder becomes that tag without the#. - Prefix match: otherwise, find all tags that equal
#<key>or start with#<key>/. The longest (most specific) match is used, stripped of#.
If no tag matches, the placeholder becomes an empty string.
Examples:
| Note's tags | Template | Resolves to |
|---|---|---|
#tasks/personal |
{{tag.tasks}} |
tasks/personal (prefix match, longest tag) |
#work, #work/design |
{{tag.work}} |
work/design (longest match) |
#inbox |
{{tag.tasks}} |
`` (empty — no match) |
#project/alpha |
{{tag.project}} |
project/alpha |
Combining static and dynamic segments
You can freely mix literal segments and placeholders:
Archive/{{property.year}}/{{property.project}}
If year: 2025 and project: Atlas, this resolves to Archive/2025/Atlas.
If year is missing, you get Archive//Atlas — a valid path with an empty segment. To avoid this, add a trigger condition that requires the property to have a value:
- Trigger:
properties→year(text) →has any value
This ensures the rule only matches (and only moves) notes where year is set.
Multiple placeholders for nested organization
{{property.year}}/{{property.client}}/{{property.project}}
This creates a three-level hierarchy purely from frontmatter. Notes with matching properties are filed exactly where they belong. Notes with any missing property resolve to a path with empty segments — consider gating the rule with has any value triggers for each property.
When the destination resolves to empty
If a rendered destination is empty or whitespace, the note is not moved. No error is shown; the note is simply skipped. This is intentional — it prevents notes with missing metadata from being dropped at the vault root.
Templates and rule matching
Templates are applied after a rule matches. They do not affect which rule wins — they only compute the final folder path. This means you can have a rule with a broad trigger (e.g. tag includes item #client) and a narrow template (using {{property.client}}). Every note with that tag is caught by the rule; the property value determines exactly where it lands.
See also
- Rules and triggers — how rules match before templates are applied
- Criteria and operators — using
has any valueto gate on properties