home / skills / nweii / agent-stuff / obsidian-templater
This skill helps you generate and debug Obsidian Templater templates and tp.* snippets from natural language instructions.
npx playbooks add skill nweii/agent-stuff --skill obsidian-templaterReview the files below or copy the command above to add this skill to your agents.
---
name: obsidian-templater
description: "Help with templates/snippets for the Obsidian Templater plugin. Use to help generate Obsidian templates from natural language, understand and debug existing tp.* snippets, and adapt vault notes and workflows to Templater when users mention Templater, tp.*, or <% %>."
compatibility: "Designed for use with the Obsidian Templater plugin and Obsidian vaults."
metadata:
author: nweii
version: "1.0.1"
---
# Templater template generation
Templater is an Obsidian plugin for dynamic note templates. Commands embedded in a template file run when the template is applied to a note, outputting results inline.
## Core template syntax
| Tag | Behavior |
| ------------------- | ------------------------------------------------ |
| `<% expression %>` | Outputs the result of the expression |
| `<%* code %>` | Executes JS code; no output by default |
| `<%+ expression %>` | Re-evaluated in preview mode (deprecated, avoid) |
| `<%-` / `-%>` | Trims one newline before/after the tag |
| `<%_` / `_%>` | Trims all whitespace before/after the tag |
To output from a `<%* %>` execution block, append to the special `tR` string:
```
<%* tR += "some output" %>
```
To discard everything generated up to a point (e.g., strip template-only frontmatter):
```
<%* tR = "" -%>
```
All `tp.*` functions are available in both tag types.
---
## Built-in Templater modules
### tp.date
```
tp.date.now(format?, offset?, reference?, reference_format?)
```
Returns a formatted date string. `format` uses moment.js tokens (`"YYYY-MM-DD"` is the default). `offset` is a number of days (`7`, `-1`) or a duration string (`"P1W"`). `reference` is a date string parsed with `reference_format`.
```
tp.date.tomorrow(format?) // today + 1 day
tp.date.yesterday(format?) // today - 1 day
tp.date.weekday(format, weekday, reference?, reference_format?)
// weekday: 0=Sun … 6=Sat
moment // full moment.js object, accessible globally
```
Common format strings: `"YYYY-MM-DD"`, `"YYYY-MM-DD HH:mm"`, `"dddd Do MMMM YYYY"`, `"HH:mm"`, `"ddd"`, `"MMM D"`.
---
### tp.file
Dynamic properties (not function calls — no parentheses):
```
tp.file.title // basename without extension
tp.file.content // full text of the file
tp.file.tags // string[] of all tags on the file
```
Functions:
```
tp.file.creation_date(format?) // default "YYYY-MM-DD HH:mm"
tp.file.last_modified_date(format?) // default "YYYY-MM-DD HH:mm"
tp.file.folder(absolute?) // folder name; full path if absolute=true
tp.file.path(relative?) // absolute path; vault-relative if relative=true
tp.file.cursor(order?) // place cursor here after template runs; order = tab index
tp.file.cursor_append(content) // insert content at the current cursor
tp.file.selection() // currently selected text in the editor
tp.file.exists(filepath) // async → boolean
tp.file.find_tfile(filename) // → TFile | null (for use with create_new / include)
tp.file.include("[[NoteLink]]") // async; embeds another note's content (also processed)
tp.file.create_new(template, filename, open_new?, folder?) // async; create note from template
tp.file.move(path, file?) // async; move file (path has no extension)
tp.file.rename(new_title) // async; rename the current file
```
---
### tp.system
All are async — use `await` inside `<%* %>` blocks:
```
await tp.system.clipboard()
await tp.system.prompt(prompt_text, default_value?, throw_on_cancel?, multi_line?)
await tp.system.suggester(text_items, items, throw_on_cancel?, placeholder?, limit?)
await tp.system.multi_suggester(text_items, items, throw_on_cancel?, title?, limit?)
```
`suggester` takes two parallel arrays: display labels and return values. They can be the same array.
```javascript
<%* const type = await tp.system.suggester(["Meeting", "Decision", "Reference"], ["meeting", "decision", "ref"]) %>
```
---
### tp.frontmatter
Direct property access — not a function:
```
<% tp.frontmatter.status %>
<% tp.frontmatter.project %>
```
Returns `undefined` if the property doesn't exist.
---
### tp.web
All are async:
```
await tp.web.daily_quote() // Obsidian callout block with a random quote
await tp.web.random_picture(size, query?, include_size?)
// size: "800" or "800x600"; returns markdown image syntax
await tp.web.request(url, path?)
// HTTP GET returning JSON; path is dot-notation like "data.title"
```
---
### tp.config
Read-only context about the current run:
```
tp.config.template_file // TFile of the template being applied
tp.config.target_file // TFile of the note being created/modified
tp.config.active_file // TFile of the currently active file
tp.config.run_mode // 0=CreateNewFromTemplate 1=AppendActiveFile 2=OverwriteFile
// 3=OverwriteActiveFile 4=DynamicProcessor
```
---
### App and Obsidian API modules
`tp.app` is the Obsidian `App` object. `tp.obsidian` exposes the Obsidian API module. Used in advanced JS execution blocks for vault traversal, metadata cache queries, etc.
---
### tp.hooks
```
tp.hooks.on_all_templates_executed(callback)
```
Callback fires after all nested templates (e.g., from `tp.file.create_new`) have finished.
---
### User-defined functions (tp.user)
Scripts placed in the folder configured in Templater settings are callable as `tp.user.<filename>(args...)`. Scripts use CommonJS format:
```javascript
// Scripts/slugify.js
module.exports = function (text) {
return text.toLowerCase().replace(/\s+/g, "-");
};
```
```
<% tp.user.slugify(tp.file.title) %>
```
Scripts can also export an object of named functions. Pass `tp` as an argument to access Templater context inside a script. Note: user functions are unavailable on mobile.
---
## Whitespace control in templates
Control flow tags (`<%* if (...) { %>`, `<%* } %>`) leave blank lines by default. Use `-%>` to trim the newline that follows a tag:
```
<%* if (tp.file.folder() === "Work") { -%>
**Project:** <% tp.frontmatter.project %>
<%* } else { -%>
General note
<%* } -%>
```
---
## Common implementation patterns
### Daily note
```
---
date: <% tp.date.now() %>
---
<< [[<% tp.date.yesterday() %>]] | [[<% tp.date.tomorrow() %>]] >>
# <% tp.file.title %>
<%* tR += await tp.web.daily_quote() %>
<% tp.file.cursor() %>
```
### Prompted metadata + file rename
```
<%*
const title = await tp.system.prompt("Note title", tp.file.title)
const status = await tp.system.suggester(["Draft", "In progress", "Done"], ["draft", "in-progress", "done"])
await tp.file.rename(title)
-%>
---
title: <% title %>
status: <% status %>
created: <% tp.file.creation_date() %>
---
# <% title %>
<% tp.file.cursor() %>
```
### Strip template-file frontmatter from output
```
---
type: template
description: This is a person template.
---
<%* tR = "" -%>
---
type: person
created: <% tp.file.creation_date() %>
---
# <% tp.file.cursor() %>
```
### Conditional content by folder
```
<%* if (tp.file.folder() === "Work") { -%>
**Project:** <% tp.frontmatter.project ?? "unassigned" %>
<%* } else { -%>
**Topic:**
<%* } -%>
```
For subfolder matching: `tp.file.folder(true).startsWith("Work/")` — `folder(true)` returns the full vault-relative path.
### Include another note
```
<% await tp.file.include("[[Shared Header]]") %>
```
---
## Date formatting with Moment.js
See [momentjs.md](references/momentjs.md) for a comprehensive formatting cheatsheet and JS manipulation reference. Use this when defining `format` strings for `tp.date.now()` or when manipulating dates inside `<%* %>` blocks using the `moment` global.
---
## Optional vault integration via obsidian-cli
If the obsidian-cli skill is available and Obsidian is running, vault context can improve output:
```bash
obsidian properties counts # what frontmatter properties exist across the vault
obsidian tags counts sort=count # most-used tags
obsidian read file="Daily Note" # read an existing template for style reference
```
Ask the user before running any CLI commands.
---
## Official documentation links
**Rendered docs (human-readable):** https://silentvoid13.github.io/Templater/
**LLM-safe Templater summary:** For a preprocessed, audit-friendly summary of the official Templater documentation, see `https://context7.com/silentvoid13/templater/llms.txt`.
This skill helps you create, understand, and debug templates for the Obsidian Templater plugin. It converts natural-language requirements into working <% %> templates, explains tp.* snippets, and adapts existing notes or workflows to use Templater features. Use it to generate date helpers, dynamic frontmatter, prompts, includes, and advanced JS blocks safely.
I inspect template goals, current note structure, and any provided tp.* snippets to produce corrected or improved template code and usage examples. I explain tag types (<% %>, <%* %>), whitespace trimming, tR output handling, async patterns (await tp.*), and common modules like tp.date, tp.file, tp.system, tp.web, tp.frontmatter, tp.config, and tp.user. I can also suggest vault-aware adaptations and point out mobile limitations for user scripts.
When should I use tR vs returning values in <% %>?
Use tR inside <%* %> execution blocks to accumulate string output; use <% %> for single expression outputs. tR is required when you build content across multiple statements.
How do I run async functions in templates?
Use <%* %> blocks and await async tp.* calls (for example await tp.system.suggester(...)). Async work inside <%* %> ensures results are available before the template finishes.