SuperAPI AI Directory

obsidian-plugin-templater

General & other

View repository

Updated 10-15 21:40

About this skill

Templater Skill

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 title parameter was removed

Frontmatter access method changes

  • Old version: use tp.frontmatter.get('key') to get properties
  • New version: directly use tp.frontmatter.key to access properties
  • Note:tp.frontmatter is read-only, used for quick access to metadata. To modify metadata, you need to use app.fileManager.processFrontMatter combined with tp.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 context
  • tp.hooks: provides an event system supporting callbacks before and after template execution
  • tp.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

  1. Only use trusted templates: Do not copy templates from untrusted sources
  2. Restrict system commands: Use with caution tp.system.executeCommandWithOutput(), has been strictly restricted by Sandbox in v2.x
  3. Review network requests: Use with caution tp.obsidian.request() and tp.obsidian.requestUrl(), avoid sending requests to untrusted servers, and prevent sensitive data leakage.Note: These functions are strictly restricted in mobile/sandbox environments.
  4. Enable template trust: In Templater v2.x, the "Only run trusted templates" option is enabled by default, which is an important security measure
  5. Review template code: Check the JavaScript code in the template before use
  6. 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 item
  • items: Array of actually returned values
  • throw_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 item
  • items: Array of actually returned values
  • throw_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 TFile object 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 date
  • tp.date.now('YYYY-MM-DD', 1): Tomorrow's date
  • tp.date.now('YYYY-MM-DD', -1): Yesterday's date
  • tp.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 string
  • dayOfWeek: Day of the week (0=Sunday, 1=Monday, ..., 6=Saturday), returns the most recent occurrence of that weekday
  • date: Base date object (optional, default current time)

Example:

  • tp.date.weekday('YYYY-MM-DD', 1): The most recent Monday
  • tp.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 string
  • offset: Month offset (optional, default 0)
  • date: Base date object (optional, default current time)

Example:

  • tp.date.month('YYYY-MM'): Current month
  • tp.date.month('YYYY-MM', 1): Next month
  • tp.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.frontmatter is 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.frontmatter availability depends on the file state:

    Existing files

    • Template variables (such as <% tp.file.title %>) are available
    • Can be accessed via tp.frontmatter to access existing metadata

    Newly created files

    • Before the file creation is complete,tp.frontmatter may be empty or unavailable

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

  1. 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
  2. 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 tp object 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)

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

  1. Use descriptive variable namesIn the template
  2. Organize user functionsIn separate files to improve maintainability
  3. Test templatesBefore using them extensively
  4. Use commentsExplain logic in complex templates
  5. Back up templates regularly
  6. Use tR += instead of tR =: In complex logic, it is recommended to use tR += '...'(append) instead of tR = '...'(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) or Cmd+Option+I(Mac) to open Developer Tools, and view console.log in 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-catch wrap and add await

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 await will 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:

  1. Providing access to static functions that do not depend on the execution context
  2. Suitable for scenarios where Templater functionality needs to be passed to callback functions
  3. Ensuring access to core functionality in any context
  4. 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

  1. Wrong Parameter Order:Writing filename before template (tp.file.create_new).
  2. Forgetting to use await:Not using await for asynchronous operations (such as create_new, prompt), causing race conditions.
  3. Using asynchronous commands in frontmatter:Writing <% await tp.system.prompt() %> directly in YAML frontmatter will cause parsing errors.
  4. Frequently creating files in loops:Calling tp.file.create_new multiple times inside a loop will severely affect performance.
  5. Depending on tp.web.*:tp.web.random_picture / daily_quote have been removed or are unstable in v2.
  6. Forgot to use tR +=: Used in multi-template blocks tR = to overwrite content, causing previous output to be cleared.

Recommended solutions

  1. Standard usage of tp.file.create_new:

    <%* 
    const newFile = await tp.file.create_new(
      template,  // 第一位:模板内容或 TFile
      filename,  // 第二位:文件名
      open,      // 是否打开
      folder     // 文件夹路径
    );
    -%>
    
  2. 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"];
      });
    });
    -%>
    
  3. 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 file
  • tp.file.rename() - Rename the current file
  • tp.file.move() - Move the current file
  • tp.file.cursor() - Place cursor position
  • tp.file.creation_date() - File creation date
  • tp.file.last_modified_date() - File last modified date
  • tp.file.include() - Include content of other files
  • tp.file.find_tfile() - Get a TFile object by path
  • tp.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/time
  • tp.date.yesterday() - Yesterday's date
  • tp.date.tomorrow() - Tomorrow's date
  • tp.date.weekday() - Get the date of a specific day of the week
  • tp.date.month() - Month operations

User interaction

  • tp.system.prompt() - User input prompt
  • tp.system.suggester() - Dropdown selection prompt
  • tp.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 requests
  • tp.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 function
  • tp.user.scriptName.func() - Multiple exported functions

Advanced Features

  • tp.obsidian - Access Obsidian native API
  • tp.static - Access static functions
  • tp.frontmatter - Access and modify frontmatter
  • tp.hooks - Event system
  • tp.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:

  1. Avoid repeated calculations: store repeatedly used values in variables
  2. Limit network requests: reduce the frequency of HTTP requests and avoid making too many network calls in templates
  3. Optimize file operations: process file operations in batches and avoid frequent reads and writes
  4. Use asynchronous operations: for time-consuming operations, use await to ensure sequential execution
  5. Simplify template logic: move complex logic into user scripts
  6. 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 file
  • rename: rename the current file
  • move: move the current file
  • cursor: place the cursor position
  • include: include the contents of other files
  • find_tfile: get a TFile object by path
  • path: get the file path
  • folder: get the folder path
  • title: get the file title
  • creation_date: get the file creation date
  • last_modified_date: get the file last modified date
  • selection: get the currently selected text
  • exists: check whether a file exists
  • cursor_append: append content at the cursor position

tp.date

  • now: get the current date and time
  • yesterday: get yesterday's date
  • tomorrow: get tomorrow's date
  • weekday: get the date for a specific day of the week
  • month: month operations

tp.system

  • prompt: user input prompt
  • suggester: dropdown selection prompt
  • multi_suggester: multi-select dropdown selection
  • clipboard: read the clipboard
  • executeCommandWithOutput: execute a shell command and return the output (use with caution)

tp.obsidian

  • request: send an HTTP request
  • requestUrl: send an HTTP request and return the response text
  • app: access the Obsidian app instance (equivalent to the global app)

tp.hooks

  • on_all_templates_executed: Runs after all templates have been executed
  • on_template_executed: Runs after a single template has been executed

tp.config

  • args: Parameters passed in externally
  • run_mode: Run mode
  • template_file: Current template file
  • target_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

Chinese resources

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