This skill enables skill-compatible agents to create and edit valid Templater templates, including all Templater-specific syntax extensions.
Version Information
This document is based on Templater v2.18.1+ . Please note:
- There are numerous breaking changes from v1.x to v2.x, and many API signatures have changed
- Older versions may not support all features in this document
- Please check your Templater version before use
Important: This document mainly introduces modern usage for v2.0+, for legacy compatibility information please refer to the compatibility section at the end of the document.
Major v1 → v2 breaking changes
- tp.file.create_new: parameter order changed (template first, filename second, optional)
- tp.system.prompt / suggester: removed the title parameter (multi_suggester has title)
- tp.frontmatter: still read-only; to modify use app.fileManager.processFrontMatter
- tp.web.random_picture / daily_quote: may be unstable or restricted (recommend avoiding reliance on third parties)
- Old tp.file.create(): removed
- Sandbox enhancements: system commands and HTTP requests are restricted on mobile/non-trusted templates
Upgrade Recommendations: Back up vault → update plugin → test templates
Version Compatibility Guide
Version Migration Guide
| Feature | Old Version (<1.10.0) | New Version (1.16.0+) |
|---|---|---|
| User input | tp.system.prompt() | tp.system.prompt() (parameters changed) |
| Frontmatter access | tp.frontmatter.get() | tp.frontmatter direct property access |
| File creation | tp.file.create() | tp.file.create_new() |
| Static functions | None | tp.static |
| Event system | None | tp.hooks |
| Template configuration | None | tp.config |
Key API Change Explanations
tp.system.prompt() parameter changes
- Old version:
tp.system.prompt(text, default, throw_on_cancel) - v1.x version:
tp.system.prompt(prompt_text: string, default_value?: string, throw_on_cancel?: boolean, multiline?: boolean, title?: string) - v2.x version:
tp.system.prompt(prompt_text?: string, default_value?: string, throw_on_cancel: boolean = false, multiline?: boolean = false) - Important: in v2.x the
titleparameter was removed
Frontmatter access method changes
- Old version: use
tp.frontmatter.get('key')to get properties - New version: directly use
tp.frontmatter.keyto access properties - Note:
tp.frontmatteris read-only, used for quick access to metadata. To modify metadata, you need to useapp.fileManager.processFrontMattercombined withtp.hooks
File creation API changes
- Old version:
tp.file.create(filename, content, open) - v1.x version:
tp.file.create_new(filename: string, template?: TFile | string, open_new?: boolean, folder?: TFolder) - v2.x version:
tp.file.create_new(template: TFile | string, filename?: string, open_new?: boolean = false, folder?: TFolder | string) - Important: in v2.x the parameter order has changed significantly, template is now the first parameter
New Features
tp.static: provides static function access that does not depend on the execution contexttp.hooks: provides an event system supporting callbacks before and after template executiontp.config: provides configuration information for template execution, including run mode and parameters
Security Warning
Templater allows the execution of arbitrary JavaScript code, which poses security risks:
Main risks
- Execute system commands:
tp.system.executeCommandWithOutput()Can execute system commands, may be exploited by malicious templates, has been strictly restricted by Sandbox in v2.x - File operations: Can read and write files in the vault
- Network requests: Can send HTTP requests to any server,
tp.obsidian.requestUrl()may bring CORS or sensitive data leakage risks, and is strictly restricted in mobile/sandbox environments
Security recommendations
- Only use trusted templates: Do not copy templates from untrusted sources
- Restrict system commands: Use with caution
tp.system.executeCommandWithOutput(), has been strictly restricted by Sandbox in v2.x - Review network requests: Use with caution
tp.obsidian.request()andtp.obsidian.requestUrl(), avoid sending requests to untrusted servers, and prevent sensitive data leakage.Note: These functions are strictly restricted in mobile/sandbox environments. - Enable template trust: In Templater v2.x, the "Only run trusted templates" option is enabled by default, which is an important security measure
- Review template code: Check the JavaScript code in the template before use
- Back up your vault: Regularly back up your Obsidian vault
Overview
Templater is a powerful plugin for Obsidian that allows you to create dynamic templates with variables, functions, and control flow. It extends Markdown through template syntax, which is processed when the template is inserted.
Basic syntax
Template variables
<% tp.file.title %> <!-- 文件标题(无扩展名) -->
<% tp.file.folder() %> <!-- 文件夹名(相对) -->
<% tp.file.folder(true) %> <!-- 绝对文件夹路径 -->
<% tp.file.path(true) %> <!-- vault 相对路径 -->
<% tp.file.content %> <!-- 执行前文件内容(只读,快照) -->
User input
Modern Templater uses tp.system.prompt for user input:
<%*
// 基本输入
const title = await tp.system.prompt('Enter a title:');
// 带默认值
const name = await tp.system.prompt('Enter your name:', 'John Doe');
// 多行输入
const description = await tp.system.prompt('Enter description:', '', false, true);
-%>
tp.system.prompt parameter description:
tp.system.prompt(prompt_text?: string, default_value?: string, throw_on_cancel: boolean = false, multiline?: boolean = false)
prompt_text: Prompt text (optional)default_value: Default value (optional)throw_on_cancel: Whether to throw an error on cancel (optional, default false)multiline: Whether to support multiline input (optional, default false; displays a text area when set to true)
Dropdown selection
<%*
// 基本用法
const selected = await tp.system.suggester(
['Option 1', 'Option 2'],
['value1', 'value2']
);
// 高级用法(带显示函数)
const item = await tp.system.suggester(
item => item.display,
[
{display: "Option 1", value: "value1"},
{display: "Option 2", value: "value2"}
]
);
tR = item.value;
-%>
tp.system.suggester parameter description:
tp.system.suggester(text_items: string[] | ((item: T) => string), items: T[], throw_on_cancel?: boolean, placeholder?: string, limit?: number)
text_items: Array of displayed text, or a function that generates text from itemitems: Array of actually returned valuesthrow_on_cancel: Whether to throw an error on cancel (optional, default false)placeholder: Input box placeholder (optional)limit: Maximum number of items displayed (optional)
Multi-select dropdown selection
Templater also provides tp.system.multi_suggester for multi-select:
<%*
const choices = await tp.system.multi_suggester(
["选项1", "选项2", "选项3"],
["val1", "val2", "val3"],
false,
"请选择...",
undefined,
"多选示例"
);
tR = `选择了: ${choices.join(', ')}`;
-%>
tp.system.multi_suggester parameter description:
tp.system.multi_suggester(
text_items: string[] | ((item: T) => string),
items: T[],
throw_on_cancel?: boolean,
placeholder?: string,
limit?: number,
title?: string
)
text_items: Array of displayed text, or a function that generates text from itemitems: Array of actually returned valuesthrow_on_cancel: Whether to throw an error on cancel (optional, default false)placeholder: Input box placeholder (optional)limit: Maximum number of items displayed (optional)title: Selection box title (optional)- The return value is an array of selected items
File creation
<%* await tp.file.create_new('Content', 'New Note.md', true) %>
<%* await tp.file.create_new('Content', 'New Note.md', true, 'Projects/Meetings') %>
<%* await tp.file.create_new(tp.file.find_tfile('Templates/Note Template.md'), 'New Note.md', true) %>
Description: The Templater v2.x API signature is create_new(template: TFile | string, filename?: string, open_new?: boolean = false, folder?: TFolder | string)
template: Template content (string) or template file (TFile object)filename: Name of the new file (optional, default "Untitled")open_new: Whether to open the newly created file (optional, default false)folder: Target folder (TFolder object or string path) (optional)
Important:tp.file.create_new What is returned is a Promise<TFile> object, which is crucial for subsequent operations that need to be performed immediately on the newly created file (such as moving or renaming).
Example Description:
- Basic Usage:
tp.file.create_new('文件内容', '文件名.md', true)- The first parameter is the template content string - Specify Folder:
tp.file.create_new('文件内容', '文件名.md', true, 'folder/path')- The first parameter is the template content string - Use Template File:
tp.file.create_new(template文件对象, '文件名.md', true)- The first parameter is the template file object
Folder Path Description:
- The folder path should use the full path relative to the vault root, such as
'Projects/Meetings' - The path depends on the user's actual folder structure
- Ensure the target folder already exists, otherwise you need to create the folder first
Important Warning:
- Avoid including self-referencing logic in template files used for
create_new, which may cause infinite creation - When using a
TFileobject as a template parameter, ensure that the file will not trigger the creation logic again
Example:
<%*
// 安全的创建方式 - 使用字符串内容
const content = `# New Note
Created on ${tp.date.now('YYYY-MM-DD')}`;
await tp.file.create_new(content, 'New Note.md', true);
-%>
<%*
// 使用模板文件
const templateFile = tp.file.find_tfile('Templates/Note Template.md');
await tp.file.create_new(templateFile, 'New Note.md', true);
-%>
Date and Time
<% tp.date.now('YYYY-MM-DD') %>
<% tp.date.now('YYYY-MM-DD HH:mm:ss') %>
<% tp.date.now('YYYY-MM-DD', 1) %> <!-- 明天的日期 -->
<% tp.date.now('YYYY-MM-DD HH:mm', 0, new Date()) %> <!-- 使用指定的 Date 对象 -->
<% tp.date.yesterday('YYYY-MM-DD') %>
<% tp.date.tomorrow('YYYY-MM-DD') %>
tp.date.now Parameter Description:
tp.date.now(format: string, offset?: number, date?: Date)
format: Date-time format string (such as 'YYYY-MM-DD')offset: Day offset (optional, default 0)date: Base date object (optional, default current time)
Example:
tp.date.now('YYYY-MM-DD'): Current datetp.date.now('YYYY-MM-DD', 1): Tomorrow's datetp.date.now('YYYY-MM-DD', -1): Yesterday's datetp.date.now('YYYY-MM-DD HH:mm', 0, new Date('2023-12-25')): Formatted result of the specified date
tp.date.weekday Parameter Description:
tp.date.weekday(format: string, dayOfWeek: number, date?: Date)
format: Date-time format stringdayOfWeek: Day of the week (0=Sunday, 1=Monday, ..., 6=Saturday), returns the most recent occurrence of that weekdaydate: Base date object (optional, default current time)
Example:
tp.date.weekday('YYYY-MM-DD', 1): The most recent Mondaytp.date.weekday('YYYY-MM-DD', 6, new Date('2023-12-25')): The most recent Saturday after December 25, 2023
tp.date.month Parameter Description:
tp.date.month(format: string, offset?: number, date?: Date)
format: Date-time format stringoffset: Month offset (optional, default 0)date: Base date object (optional, default current time)
Example:
tp.date.month('YYYY-MM'): Current monthtp.date.month('YYYY-MM', 1): Next monthtp.date.month('YYYY-MM', -1): Previous month
Silent Execution (Preserve Newlines)
Templater supports multiple execution modes, which can be used in combination with the +(preserve newlines) and -(remove subsequent newlines) modifiers:
Output Command
| Syntax | Description |
|---|---|
<% ... %> |
Output command, does not preserve newlines |
<%+ ... %> |
Output command, preserves newlines |
<% ... -%> |
Output command, removes subsequent newlines |
<%+ ... -%> |
Output command, preserves newlines but removes subsequent extra newlines |
Execution Command
| Syntax | Description |
|---|---|
<%* ... %> |
Execution command, does not preserve newlines |
<%*+ ... %> |
Execution command, preserves newlines |
<%* ... -%> |
Execution command, removes subsequent newlines |
<%*+ ... -%> |
Execution command, preserves newlines but removes subsequent extra newlines |
<%+ tp.date.now('YYYY-MM-DD') %>
<%*+
// 执行 JavaScript 代码并保留换行
const today = new Date();
tR = `今天是 ${today.toLocaleDateString()}`;
-%>
Description:+ The modifier is used to preserve blank lines in the template and prevent the output from being crammed together;- The modifier is used to remove extra line breaks after the tag, making the output more compact. This is very useful when you need to keep the template structure clear.
Execution mode (modifier)
| Syntax | Output | Preserve line breaks | Remove subsequent line breaks |
|---|---|---|---|
<% ... %> |
Yes | No | No |
<%+ ... %> |
Yes | Yes | No |
<% ... -%> |
Yes | No | Yes |
<%+ ... -%> |
Yes | Yes | Yes |
<%* ... %> |
No | No | No |
<%*+ ... %> |
No | Yes | No |
<%* ... -%> |
No | No | Yes |
<%*+ ... -%> |
No | Yes | Yes |
Advanced syntax
JavaScript execution
<%*
const today = new Date();
const formattedDate = today.toISOString().split('T')[0];
tp.file.rename(`Note ${formattedDate}`);
-%>
Control flow
<%*
// 直接使用 tR,不要声明
if (tp.file.title.includes('Meeting')) {
tR = 'This is a meeting note.';
} else {
tR = 'This is a regular note.';
}
-%>
<%*
tR = '';
for (let i = 1; i <= 5; i++) {
tR += `- Item ${i}\n`;
}
-%>
Best practices: In complex logic blocks, you should always recommend using tR += '...'(accumulate) rather than tR = '...'(overwrite), unless the explicit intent is to clear previous content. This protects previously generated text from being accidentally overwritten.
Example:
<%*
let message = "Hello";
tR += `${message} World\n`; // 使用 += 而不是 =
%>
Function
<% tp.file.cursor() %>
<% tp.file.rename('New Title.md') %>
<% tp.file.move('New Folder/New Title.md') %>
<% tp.system.executeCommandWithOutput('echo "Hello World"') %>
<% tp.user.myFunction() %>
Frontmatter
<%*
const tag = await tp.system.prompt('Enter a tag:');
-%>
---
title: <% tp.file.title %>
date: <% tp.date.now('YYYY-MM-DD') %>
tags:
- template
- <% tag %>
---
tp.frontmatter
Modern Templater provides the tp.frontmatter object toread-onlyaccess the current file's frontmatter:
<%*
// 读取 frontmatter
const title = tp.frontmatter.title;
const tags = tp.frontmatter.tags;
%>
Important notes:
-
tp.frontmatteris a read-only object, used only for quick access to metadata -
Using asynchronous commands (such as
await tp.system.prompt()) directly in YAML frontmatter will cause parsing errors -
It is recommended to use a JavaScript execution block at the beginning of the template to collect user input, and then use variables in the frontmatter
-
Key limitations:
tp.frontmatteravailability depends on the file state:Existing files
- Template variables (such as
<% tp.file.title %>) are available - Can be accessed via
tp.frontmatterto access existing metadata
Newly created files
- Before the file creation is complete,
tp.frontmattermay be empty or unavailable
- Template variables (such as
Correct way to modify Frontmatter:
To modify Frontmatter, you need to combine tp.hooks.on_all_templates_executed with Obsidian's app.fileManager.processFrontMatter:
<%*
tp.hooks.on_all_templates_executed(async () => {
const file = tp.file.find_tfile(tp.file.path(true));
await app.fileManager.processFrontMatter(file, (fm) => {
fm["status"] = "active";
fm["priority"] = "high";
// 添加标签
if (!fm.tags) fm.tags = [];
if (!fm.tags.includes("new-tag")) fm.tags.push("new-tag");
// 移除标签
fm.tags = fm.tags.filter(tag => tag !== "old-tag");
});
});
%>
Obsidian integration
tp.obsidian API
Templater provides the tp.obsidian object to access Obsidian's native API for advanced operations.tp.obsidian.app and the global app object are equivalent; both are references pointing to the Obsidian app instance.
tp.config
Modern Templater provides the tp.config object to access template configuration information:
<%*
// 获取当前模板文件
const templateFile = tp.config.template_file;
tR = `Template: ${templateFile.name}`;
// 获取目标文件
const targetFile = tp.config.target_file;
if (targetFile) {
tR += `\nTarget: ${targetFile.name}`;
}
// 获取运行模式
const runMode = tp.config.run_mode;
tR += `\nRun mode: ${runMode}`;
%>
tp.config property description:
template_file: current template file (TFile object)target_file: target file (TFile object, may be undefined)run_mode: run mode (string, such as "CreateNewNote", "InsertTemplate", etc.)args: parameters passed to the template (object, may be undefined)
Detailed description of tp.config.args:
- When a template is invoked via a command and parameters are passed, the parameters are stored in this object
- For example, parameters passed when called via the Templater URI are parsed into this object
- It can be used to build templates that accept external parameters, enabling more flexible template behavior
Purpose:
- Debug the template execution environment
- Execute different logic depending on the run mode
- Build conditional template behavior
- Handle externally passed parameters
Example:
<%*
// 检查是否有传递的参数
if (tp.config.args) {
const title = tp.config.args.title || 'Default Title';
const tags = tp.config.args.tags || ['template'];
tR = `Title: ${title}\nTags: ${tags.join(', ')}`;
} else {
tR = 'No arguments provided';
}
-%>
<%*
// 获取当前保险库中的所有文件
const files = app.vault.getFiles(); // 推荐使用全局 app 对象
tR = `保险库中有 ${files.length} 个文件`;
-%>
<%*
// 获取文件创建时间和修改时间
const creationDate = tp.file.creation_date('YYYY-MM-DD');
const modifiedDate = tp.file.last_modified_date('YYYY-MM-DD');
tR = `创建时间: ${creationDate}\n`;
tR += `修改时间: ${modifiedDate}`;
-%>
HTTP Request
Templater supports making HTTP requests via tp.obsidian :
<%*
// 发送 HTTP 请求(返回响应对象)
const response = await tp.obsidian.request({
url: 'https://api.example.com/data',
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
tR = `响应状态: ${response.status}`;
-%>
<%*
// 包含授权头的请求示例
const apiToken = "your_api_token_here";
const response = await tp.obsidian.request({
url: 'https://api.example.com/data',
method: 'GET',
headers: {
'Authorization': `Bearer ${apiToken}`,
'Content-Type': 'application/json',
'Accept': 'application/json'
}
});
tR = `响应状态: ${response.status}`;
-%>
<%*
// 发送 HTTP 请求(返回响应文本)
const responseText = await tp.obsidian.requestUrl('https://api.example.com/data');
tR = responseText;
-%>
HTTP Request Error Handling
Important: HTTP requests may fail due to network issues, server errors, and other reasons, so try-catch should be used for handling:
<%*
try {
const response = await tp.obsidian.request({
url: 'https://api.example.com/data',
method: 'GET'
});
if (response.status === 200) {
const data = JSON.parse(response);
tR = `成功获取数据: ${data.message}`;
} else {
tR = `请求失败: ${response.status}`;
}
} catch (error) {
console.error('网络错误:', error);
new app.Notice('无法获取数据,请检查网络连接', 5000);
tR = `错误: ${error.message}`;
}
-%>
User Functions
Creating User Functions
-
Configure the User Scripts Folder:
Step 1: Create a Folder
- Create a folder in the Obsidian vault, such as
scripts/
Step 2: Set the Path
- Open Obsidian Settings (⚙️)
- Navigate to:
社区插件→Templater→设置 - Enter the folder path in the "User Scripts Folder" field, such as
scripts/ - Click "OK" to save the settings
Note:
- Templater does not have a preset user scripts folder by default
- The path is relative to the vault root directory
- Make sure the folder name matches the path in the settings
- Create a folder in the Obsidian vault, such as
-
Create a JavaScript File(for example:
scripts/myFunctions.js):
// scripts/myFunctions.js - 导出多个函数
module.exports = {
greet: function(tp, name) {
return `Hello, ${name}! Current file: ${tp.file.title}`;
},
getRandomNumber: function(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
};
// 或者 - 导出单个函数
function mainFunction(tp) {
return tp.file.title;
}
module.exports = mainFunction;
Important Notes:
- The tp parameter must be passed: User functions need to receive the
tpobject as the first parameter in order to use Templater features inside the function - Calling Different Export Methods:
- Export multiple functions:
tp.user.scriptName.functionName(tp, ...) - Export a single function:
tp.user.scriptName(tp)
- Export multiple functions:
Using User Functions
<%*
// 调用导出多个函数的脚本
const greeting = tp.user.myFunctions.greet(tp, 'World');
tR = greeting;
-%>
<%*
// 调用导出单个函数的脚本
const title = tp.user.mainFunction(tp);
tR = title;
-%>
Template Examples
Daily Note Template
---
title: <% tp.date.now('YYYY-MM-DD') %>
date: <% tp.date.now('YYYY-MM-DD HH:mm:ss') %>
tags:
- daily
---
<%*
const today = tp.date.now('YYYY-MM-DD');
const dayName = tp.date.now('dddd');
tR = `# ${today} ${dayName}\n\n`;
tR += "## Tasks\n\n";
tR += "- [ ] \n\n";
tR += "## Notes\n\n\n";
tR += "## Reflection\n\n\n";
tR += "## Tomorrow's Tasks\n\n";
tR += "- [ ] \n\n";
-%>
<% tp.file.cursor() %>
Meeting Note Template
<%*
const meetingTitle = await tp.system.prompt('Meeting title:');
const meetingType = await tp.system.prompt('Meeting type:');
const attendees = await tp.system.prompt('Attendees (comma-separated):');
-%>
---
title: Meeting - <% meetingTitle %>
date: <% tp.date.now('YYYY-MM-DD HH:mm:ss') %>
tags:
- meeting
- <% meetingType %>
attendees: "<% attendees %>"
---
# Meeting - <% meetingTitle %>
## Date & Time
<%*
const start = new Date();
const end = new Date(start.getTime() + 60 * 60 * 1000);
// 使用tp.date.now格式化日期,避免依赖全局moment对象
const startTime = tp.date.now('YYYY-MM-DD HH:mm', 0, start);
const endTime = tp.date.now('YYYY-MM-DD HH:mm', 0, end);
tR = `${startTime} - ${endTime}`;
-%>
## Attendees
-
## Agenda
-
## Notes
## Decisions
-
## Action Items
- [ ]
## Next Steps
-
<% tp.file.cursor() %>
Project Note Template
<%*
const projectTitle = await tp.system.prompt('Project title:');
const projectType = await tp.system.prompt('Project type:');
-%>
---
title: <% projectTitle %>
date: <% tp.date.now('YYYY-MM-DD') %>
tags:
- project
- <% projectType %>
status: planning
priority: medium
---
# <% projectTitle %>
## Overview
## Goals
-
## Timeline
### Milestones
- [ ]
### Tasks
- [ ]
## Resources
-
## Notes
<% tp.file.cursor() %>
Best Practices
- Use descriptive variable namesIn the template
- Organize user functionsIn separate files to improve maintainability
- Test templatesBefore using them extensively
- Use commentsExplain logic in complex templates
- Back up templates regularly
- Use tR += instead of tR =: In complex logic, it is recommended to use
tR += '...'(append) instead oftR = '...'(overwrite), especially in the case of new files + multiple template blocks, as this avoids accidentally overwriting already generated content
Error Handling and Debugging
Debugging Tips
<%*
// 在控制台输出调试信息(在 Obsidian 开发者工具中查看)
console.log('Debug info:', tp.file.title);
console.log('Frontmatter:', tp.frontmatter);
// 使用 try-catch 处理错误
try {
const result = await tp.file.include("[[Template]]");
tR = result;
} catch (error) {
console.error('Error:', error);
tR = `Error: ${error.message}`;
// 显示用户友好的错误消息
new app.Notice(`Template error: ${error.message}`, 5000);
}
%>
Error Handling Best Practices
Handle the case where a file does not exist
<%*
// 处理文件不存在的情况
const filePath = "Notes/Example.md";
try {
const file = app.vault.getAbstractFileByPath(filePath);
if (!file) {
throw new Error(`文件不存在: ${filePath}`);
}
const fileContent = await app.vault.read(file);
tR = fileContent;
} catch (error) {
console.error(`读取文件错误: ${error.message}`);
new app.Notice(`错误: ${error.message}`, 5000);
tR = `⚠️ 无法加载内容`;
}
%>
Handle the case where the user cancels input
<%*
try {
const title = await tp.system.prompt('Enter a title:', '', true);
if (!title) {
throw new Error('用户取消了输入');
}
tR = `Title: ${title}`;
} catch (error) {
console.error('输入错误:', error.message);
new app.Notice('操作已取消', 3000);
tR = '⚠️ 操作已取消';
}
%>
Handle network errors
<%*
try {
const response = await tp.obsidian.request({
url: 'https://api.example.com/data',
method: 'GET'
});
tR = `Response status: ${response.status}`;
} catch (error) {
console.error('网络错误:', error.message);
new app.Notice('网络请求失败,请检查网络连接', 5000);
tR = '⚠️ 网络请求失败';
}
%>
Debugging Notes:
- Developer Tools: In Obsidian, press
Ctrl+Shift+I(Windows/Linux) orCmd+Option+I(Mac) to open Developer Tools, and viewconsole.login the Console tab - Notice:
new Notice(message, timeout)is used to display temporary messages in the Obsidian interface, improving the user experience - Asynchronous Debugging:For asynchronous operations, remember to use
try-catchwrap and addawait
async support
Templater supports await keyword to handle asynchronous operations:
<%*
// 异步包含其他模板
const content = await tp.file.include("[[Template]]");
tR = content;
// 异步获取文件内容
const file = app.vault.getAbstractFileByPath("Notes/Example.md");
const fileContent = await app.vault.read(file);
tR += fileContent;
%>
Asynchronous Execution Order
Important Notes:
- Execution Mechanism:Templater first parses all
<%* %>blocks and<% %>blocks in the document, then executes them in the order they appear in the document (similar to variable hoisting) - Asynchronous operations (using
await) will block subsequent execution until they complete - Multiple asynchronous commands will execute in order, ensuring consistency of operations
- Asynchronous operations that do not use
awaitwill execute in parallel, which may lead to unpredictable results - Asynchronous operations in an execution block will affect the execution progress of the entire template, ensuring the order of operations
- Note:The execution order may not be exactly consistent with the intuitive top-to-bottom reading order, especially in complex templates
Web API Notes
Important:tp.web -related functions (such as random_picture()、daily_quote()) have been completely removed in Templater v2.x (for security reasons). These functions are no longer available in v2.0+.
tp.static
Modern Templater provides the tp.static object to access static functions, mainly used for:
- Providing access to static functions that do not depend on the execution context
- Suitable for scenarios where Templater functionality needs to be passed to callback functions
- Ensuring access to core functionality in any context
- Accessing functions in user scripts (User Scripts) that normally require an active editor context to call
Important Notes:tp.static object still needs to be used within a JavaScript execution block (<%*), but the functions it provides can be passed to other functions or callbacks for use. It cannot be used directly outside an execution block (such as <%).
Usage Examples in User Scripts:
// scripts/myFunctions.js - 在用户脚本中使用 tp.static
module.exports = {
formatDate: function(date, tpStatic) {
// 在用户脚本中,无法直接访问 tp 对象
// 但可以通过传递 tp.static 来使用 Templater 的日期函数
return tpStatic.date.now('YYYY-MM-DD', 0, date);
}
};
Calling in Templates:
<%*
const formattedDate = tp.user.myFunctions.formatDate(new Date(), tp.static);
tR = formattedDate;
-%>
<%*
// 在执行块内使用
const now = tp.static.date.now('YYYY-MM-DD');
tR = now;
-%>
<%*
// 作为参数传递
function formatDate(date, tpStatic) {
return tpStatic.date.now('YYYY-MM-DD', 0, date);
}
const formatted = formatDate(new Date(), tp.static);
tR = formatted;
-%>
tp.hooks
Templater provides the tp.hooks object to register event listeners for responding to different stages of template execution.The most central Hook is tp.hooks.on_all_templates_executed(callback), which is the only safe way to handle cleanup work after template execution (such as modifying properties of the just-generated file).
Important:Hook registration should be placed at the top level of the template code as much as possible. If a Hook is registered only in complex logic (such as after a conditional check), and the trigger point has already passed, the callback will never execute.
<%*
// 注册事件监听器
tp.hooks.on('on_all_templates_executed', () => {
// 所有模板执行完成后执行的清理操作
console.log('All templates executed');
});
// 注册一次性事件监听器
tp.hooks.once('on_template_executed', (templateFile, targetFile) => {
console.log('Template executed:', templateFile.name, '→', targetFile.name);
});
%>
Common Errors and Pitfalls
Common Pitfalls in Template Development
- Wrong Parameter Order:Writing
filenamebeforetemplate(tp.file.create_new). - Forgetting to use await:Not using await for asynchronous operations (such as create_new, prompt), causing race conditions.
- Using asynchronous commands in frontmatter:Writing
<% await tp.system.prompt() %>directly in YAML frontmatter will cause parsing errors. - Frequently creating files in loops:Calling
tp.file.create_newmultiple times inside a loop will severely affect performance. - Depending on tp.web.*:tp.web.random_picture / daily_quote have been removed or are unstable in v2.
- Forgot to use tR +=: Used in multi-template blocks
tR =to overwrite content, causing previous output to be cleared.
Recommended solutions
-
Standard usage of tp.file.create_new:
<%* const newFile = await tp.file.create_new( template, // 第一位:模板内容或 TFile filename, // 第二位:文件名 open, // 是否打开 folder // 文件夹路径 ); -%> -
Safe ways to modify frontmatter:
<%* tp.hooks.on_all_templates_executed(async () => { const file = tp.file.find_tfile(tp.file.path(true)); await app.fileManager.processFrontMatter(file, (fm) => { fm.status = "active"; fm.tags = [...(fm.tags || []), "new"]; }); }); -%> -
Batch file operations: Pre-generate content before creating files in batches to reduce I/O operations.
The importance of tp.hooks.on_all_templates_executed
tp.hooks.on_all_templates_executed is the most important and safest post-template-execution mechanism in Templater v2. Its core advantages:
- Execution timing: Runs after all template code has finished executing, ensuring the file has been fully created.
- Safety: Provides a safe context for modifying the newly created file (such as modifying frontmatter).
- Avoiding race conditions: Avoids the file-not-ready problem caused by the order of asynchronous operations.
Recommended usage:
- Modify the frontmatter of a newly created file
- Perform follow-up operations on new files (such as moving, renaming)
- Execute logic that requires the file to be fully ready
Example:
<%*
// 在模板开始处注册 Hook(推荐)
tp.hooks.on_all_templates_executed(async () => {
// 安全地修改刚创建的文件
const file = tp.file.find_tfile(tp.file.path(true));
await app.fileManager.processFrontMatter(file, (fm) => {
fm.createdAt = tp.date.now('YYYY-MM-DD');
});
// 显示完成通知
new app.Notice('模板执行完成,文件已更新!', 5000);
});
// 其他模板逻辑...
-%>
Detailed explanation of the event system:
| Event | Trigger condition | Parameters | Use cases |
|---|---|---|---|
on_template_create |
When a template file is created | (templateFile: TFile) |
Initialize a new template, add default content |
on_template_delete |
When a template file is deleted | (templateFile: TFile) |
Clean up resources related to the template |
on_template_executed |
After a single template finishes executing | (templateFile: TFile, targetFile: TFile) |
Record template usage, perform follow-up operations |
on_all_templates_executed |
After all templates finish executing | () |
Perform global cleanup, display completion messages, modify properties of just-generated files |
Core usage example: Use on_all_templates_executed to modify properties of a just-generated file
<%*
tp.hooks.on_all_templates_executed(async () => {
// 确保文件已完全创建
const file = tp.file.find_tfile(tp.file.path(true));
if (file) {
// 修改文件的 frontmatter
await app.fileManager.processFrontMatter(file, (fm) => {
fm.status = 'active';
fm.priority = 'high';
});
}
});
%>
Example: Recording template execution
<%*
tp.hooks.on('on_template_executed', async (templateFile, targetFile) => {
const logFile = app.vault.getAbstractFileByPath('Templater Log.md');
if (logFile) {
const content = await app.vault.read(logFile);
const newContent = `${content}\n- ${new Date().toISOString()}: ${templateFile.name} → ${targetFile.name}`;
await app.vault.modify(logFile, newContent);
}
});
%>
Purpose:
- Perform cleanup operations
- Record template execution history
- Trigger follow-up operations
- Implement complex template workflows
Best practices for asynchronous execution
Recommended practices:
<%*
// 按顺序执行多个异步操作
const content1 = await tp.file.include("[[Template1]]");
const content2 = await tp.file.include("[[Template2]]");
tR = content1 + "\n\n" + content2;
%>
Reference
Functional categories
To organize Templater's functionality more clearly, here is the API reference grouped by category:
File operations
tp.file.create_new()- Create a new filetp.file.rename()- Rename the current filetp.file.move()- Move the current filetp.file.cursor()- Place cursor positiontp.file.creation_date()- File creation datetp.file.last_modified_date()- File last modified datetp.file.include()- Include content of other filestp.file.find_tfile()- Get a TFile object by pathtp.file.selection()- Get selected text (only effective when inserting a template into an existing file and text is selected)
Date and time
tp.date.now()- Current date/timetp.date.yesterday()- Yesterday's datetp.date.tomorrow()- Tomorrow's datetp.date.weekday()- Get the date of a specific day of the weektp.date.month()- Month operations
User interaction
tp.system.prompt()- User input prompttp.system.suggester()- Dropdown selection prompttp.system.clipboard()- Read clipboard
System operations
tp.system.executeCommandWithOutput()- Execute shell commands and return output- Important Warning: This function is strictly restricted by the Sandbox in Templater v2.x. In most modern environments (especially mobile and sandbox mode), this function does not work properly, and it is strongly recommended not to use it in production templates.
Web Operations
tp.obsidian.request()- Send HTTP requeststp.obsidian.requestUrl()- Send HTTP requests and return response text
Note:tp.web Related functions (such as random_picture()、daily_quote()) have been completely removed in Templater v2.x (for security reasons).
User Scripts
tp.user.scriptName()- Single exported functiontp.user.scriptName.func()- Multiple exported functions
Advanced Features
tp.obsidian- Access Obsidian native APItp.static- Access static functionstp.frontmatter- Access and modify frontmattertp.hooks- Event systemtp.config- Template configuration
Templater Variables
| Variable | Description | Example |
|---|---|---|
tp.file.title |
Current file title | <% tp.file.title %> |
tp.file.folder() |
Current file folder (relative path) | <% tp.file.folder() %> |
tp.file.folder(true) |
Current file folder (absolute path) | <% tp.file.folder(true) %> |
tp.file.path() |
Current file path (relative path) | <% tp.file.path() %> |
tp.file.path(true) |
Current file path (absolute path) | <% tp.file.path(true) %> |
tp.file.content |
Current file content (only valid when executing a template on an existing file; empty when creating a new file).Note: This variable is fixed at the start of template execution and only contains the file content on disk before template execution; it does not reflect dynamic changes during execution. | <% tp.file.content %> |
tp.frontmatter.tags |
Array of file tags | <% tp.frontmatter.tags %> |
tp.frontmatter |
Frontmatter of the current file | <% tp.frontmatter.title %> |
Templater Functions
| Function | Description | Example | Asynchronous |
|---|---|---|---|
tp.date.now() |
Current date/time | <% tp.date.now('YYYY-MM-DD') %> |
❌ |
tp.date.yesterday() |
Yesterday's date | <% tp.date.yesterday('YYYY-MM-DD') %> |
❌ |
tp.date.tomorrow() |
Tomorrow's date | <% tp.date.tomorrow('YYYY-MM-DD') %> |
❌ |
tp.date.weekday() |
Get the date of a specific weekday | <% tp.date.weekday('YYYY-MM-DD', 1) %> |
❌ |
tp.date.month() |
Month operations | <% tp.date.month('YYYY-MM-DD', 1) %> |
❌ |
tp.file.create_new() |
Create a new file | <%* await tp.file.create_new('Content', 'New Note.md', true); -%> |
✅ |
tp.file.rename() |
Rename the current file | <% tp.file.rename('New Title.md') %> |
❌ |
tp.file.move() |
Move the current file | <% tp.file.move('New Folder/New Title.md') %> |
❌ |
tp.file.cursor() |
Place the cursor position.Note: This function can accept an optional numeric parameter, such as <% tp.file.cursor(1) %>, <% tp.file.cursor(2) %>, allowing users to jump between multiple preset cursor points via keyboard shortcuts. |
<% tp.file.cursor(1) %> |
❌ |
tp.file.cursor_append() |
Append content at the current cursor position (commonly used in multi-cursor scenarios) | <% tp.file.cursor_append('追加内容') %> |
❌ |
tp.file.creation_date() |
File creation date | <% tp.file.creation_date('YYYY-MM-DD') %> |
❌ |
tp.file.last_modified_date() |
File last modified date | <% tp.file.last_modified_date('YYYY-MM-DD') %> |
❌ |
tp.file.include() |
Include content of other files | <%* tR = await tp.file.include('[[Template]]'); -%> |
✅ |
tp.file.find_tfile() |
Get a TFile object by path | <% tp.file.find_tfile('Notes/Example.md') %> |
❌ |
tp.file.selection() |
Get the selected text (only valid when inserting a template into an existing file and text is selected, returns the selected string or empty) | <% tp.file.selection() %> |
❌ |
tp.file.exists() |
Check whether a file/folder exists, returns boolean (synchronous) | <% tp.file.exists('Folder/Note.md') %> |
❌ |
tp.system.prompt() |
User input prompt | <% tp.system.prompt('Enter a title:', 'Default', false, true) %> |
❌ |
tp.system.suggester() |
Dropdown selection prompt | <% tp.system.suggester(item => item.display, [{display: "Option 1", value: "value1"}]) %> |
❌ |
tp.system.clipboard() |
Read clipboard | <%* tR = await tp.system.clipboard(); -%> |
✅ |
tp.system.executeCommandWithOutput() |
Execute shell commands and return output | <% tp.system.executeCommandWithOutput('echo "Hello"') %> |
❌ |
| tp.user.scriptName() | Single exported function | <% tp.user.myFunction(tp) %> | ❌ |
| tp.user.scriptName.func() | Multiple exported functions | <% tp.user.myModule.greet(tp, 'World') %> | ❌ |
| tp.obsidian | Access Obsidian native API | <% tp.obsidian.app.vault.getFiles() %> | ❌ |
| tp.obsidian.request() | Send HTTP requests | <% tp.obsidian.request({url: 'https://api.example.com'}) %> | ✅ |
| tp.obsidian.requestUrl() | Send an HTTP request and return the response text | <% tp.obsidian.requestUrl('https://api.example.com') %> | ✅ |
| tp.static | Access static functions | <% tp.static.date.now('YYYY-MM-DD') %> | ❌ |
Workflow example
Complex workflow example
The following is an example of a complex workflow that combines multiple templates and conditional logic:
<%*
// 步骤 1: 收集用户输入
const projectType = await tp.system.suggester(
['个人项目', '团队项目', '客户项目'],
['personal', 'team', 'client']
);
const projectName = await tp.system.prompt('项目名称:', '', true);
const hasDeadline = await tp.system.prompt('有截止日期吗?', 'yes', true, false).toLowerCase() === 'yes';
let deadline = '';
if (hasDeadline) {
deadline = await tp.system.prompt('截止日期 (YYYY-MM-DD):');
}
// 步骤 2: 创建项目文件夹
const folderPath = `Projects/${projectType}/${projectName}`;
const folder = app.vault.getAbstractFileByPath(folderPath);
if (!folder) {
await app.vault.createFolder(folderPath);
}
// 步骤 4: 创建项目文件
const projectContent = `---
title: ${projectName}
type: ${projectType}
date: ${tp.date.now('YYYY-MM-DD')}
${deadline ? `deadline: ${deadline}` : ''}
status: planning
---
# ${projectName}
## 概述
## 任务
- [ ] 项目初始化
- [ ] 需求分析
- [ ] 设计阶段
- [ ] 开发阶段
- [ ] 测试阶段
- [ ] 交付
## 资源
## 备注
`;
await tp.file.create_new(projectContent, `${projectName}.md`, true, folderPath);
// 步骤 5: 创建会议记录模板
const meetingContent = `---
title: ${projectName} - 会议记录
date: <% tp.date.now('YYYY-MM-DD HH:mm:ss') %>
type: meeting
project: ${projectName}
---
# ${projectName} - 会议记录
## 参会人员
-
## 议程
-
## 讨论内容
## 行动项
- [ ]
## 下次会议
`;
await tp.file.create_new(meetingContent, `${projectName} - 会议记录模板.md`, false, `${folderPath}/Meeting Notes`);
// 步骤 5: 显示完成消息
new app.Notice(`项目 ${projectName} 创建完成!`, 5000);
%>
Note: when the template parameter is a string and contains <% %> , Templater will execute the template syntax inside it a second time. This is expected behavior, used for dynamic template nesting, but if the template content comes from user input or an external source, it may lead to unexpected execution.
Recommendation: for complex templates, it is recommended to use a TFile object as the template parameter (for example tp.file.find_tfile('Templates/Meeting Note.md')), to avoid the potential risk of nested execution.
Performance optimization
When using Templater, please note the following performance best practices:
- Avoid repeated calculations: store repeatedly used values in variables
- Limit network requests: reduce the frequency of HTTP requests and avoid making too many network calls in templates
- Optimize file operations: process file operations in batches and avoid frequent reads and writes
- Use asynchronous operations: for time-consuming operations, use
awaitto ensure sequential execution - Simplify template logic: move complex logic into user scripts
- Use caching: for repeatedly used external data, consider using caching
Specific performance optimization suggestions
Avoid file I/O operations in loops
File I/O operations are time-consuming and should be avoided repeatedly in loops:
<%*
// 不推荐的做法
const items = ['item1', 'item2', 'item3'];
for (const item of items) {
// 每次循环都进行文件操作
await tp.file.create_new(`Content for ${item}`, `${item}.md`);
}
// 推荐的做法
const items = ['item1', 'item2', 'item3'];
// 先准备所有内容
const contents = items.map(item => `Content for ${item}`);
// 批量创建文件
for (let i = 0; i < items.length; i++) {
await tp.file.create_new(contents[i], `${items[i]}.md`);
}
%>
Limit HTTP request frequency
Reduce the frequency of HTTP requests and avoid making too many network calls in templates:
<%*
// 不推荐的做法
const urls = ['https://api.example.com/data1', 'https://api.example.com/data2'];
const results = [];
for (const url of urls) {
// 每次循环都发送HTTP请求
const response = await tp.obsidian.request({ url });
results.push(response);
}
// 推荐的做法
// 考虑使用批量API端点
const response = await tp.obsidian.request({
url: 'https://api.example.com/batch',
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ endpoints: ['data1', 'data2'] })
});
%>
Avoid complex calculations in large templates
Move complex calculations into user scripts and simplify template logic:
<%*
// 不推荐的做法
// 在模板中进行复杂计算
const data = await tp.obsidian.request({ url: 'https://api.example.com/data' });
const processedData = data.map(item => {
// 复杂的处理逻辑
return {
id: item.id,
value: item.value * 2,
status: item.value > 100 ? 'high' : 'low'
};
});
// 推荐的做法
// 在用户脚本中进行复杂计算
const processedData = await tp.user.processData(tp, 'https://api.example.com/data');
%>
Batch operations are better than multiple single operations
Use batch operations instead of multiple single operations to reduce the number of API calls:
<%*
// 推荐的做法:在一个 processFrontMatter 回调中完成所有修改
await app.fileManager.processFrontMatter(tp.file.find_tfile(tp.file.path(true)), (fm) => {
fm.status = 'active';
fm.priority = 'high';
fm.tags = ['project', 'important'];
});
%>
Reference API categories (v2.18.1+)
tp.file
create_new: create a new filerename: rename the current filemove: move the current filecursor: place the cursor positioninclude: include the contents of other filesfind_tfile: get a TFile object by pathpath: get the file pathfolder: get the folder pathtitle: get the file titlecreation_date: get the file creation datelast_modified_date: get the file last modified dateselection: get the currently selected textexists: check whether a file existscursor_append: append content at the cursor position
tp.date
now: get the current date and timeyesterday: get yesterday's datetomorrow: get tomorrow's dateweekday: get the date for a specific day of the weekmonth: month operations
tp.system
prompt: user input promptsuggester: dropdown selection promptmulti_suggester: multi-select dropdown selectionclipboard: read the clipboardexecuteCommandWithOutput: execute a shell command and return the output (use with caution)
tp.obsidian
request: send an HTTP requestrequestUrl: send an HTTP request and return the response textapp: access the Obsidian app instance (equivalent to the global app)
tp.hooks
on_all_templates_executed: Runs after all templates have been executedon_template_executed: Runs after a single template has been executed
tp.config
args: Parameters passed in externallyrun_mode: Run modetemplate_file: Current template filetarget_file: Target file
tp.frontmatter
- Read-only object used to access the file's frontmatter properties (such as
.title,.tags, etc.)
tp.static
- Statically access Templater functionality (for callback functions)
References
- Templater official documentation(English)
- Templater GitHub repository(English)
- Obsidian Forum - Templater(English)
Chinese resources
- Obsidian Chinese community - Includes Templater-related discussions
- Templater Chinese tutorial - Templater tutorial on Zhihu
- Obsidian plugin usage guide - Chinese plugin usage guide
- Templater Chinese community discussion group - Chinese discussion focused on Templater
- Templater plugin configuration video tutorial - Templater configuration tutorial on Bilibili
- Obsidian template advanced tips - Advanced template usage tips on Bilibili
Terminology comparison
| English | Chinese |
|---|---|
| Templater | Templater (plugin name, keep in English) |
| Template | Template |
| Variable | Variable |
| Function | Function |
| Control Flow | Control flow |
| Frontmatter | Frontmatter (YAML properties) |
| User Scripts | User script |
| API | Application programming interface |
| Async/Await | Async/await |
| Console | Console |
| Vault | Vault |
| Obsidian | Obsidian (keep in English) |
| Execute Command | Execution command |
| Output Command | Output command |