This article concerns the extremely powerful Templater plugin for Obsidian. To a lesser extent it is about the Eta JS templating engine that drives the Templater plugin and the fun of learning new things.
I have multiple directories in my Obsidan vault. I have a folder for work stuff and a folder for general notes. I also have a folder for Macdrifter drafts. You get the idea. There’s a bunch of folders but when I tell Obsidian to create a new file I only get one option for the default note location. This is frustrating. So, with a tiny bit of research and key mashing I created a template that does the following:
- Ask me for a note title
- Create a new note with the title containing a date suffix
- Add some front matter that I always want to include in notes
- Move the note to the right folder
- Show me the note ready to edit with the cursor blinking away at the bottom
Templater is a plugin for Obsidian that adds support for the Eta templating engine. Templates are simple markdown documents where JS snippets are included alongside plain text. These snippets execute when Templater runs a template. The functions in Templater range from simple text insertion to manipulating the Obsidian application itself. Templater can move files, rename them, add new content, and do pretty much anything I want to do inside Obsidian.
Let’s get to the good stuff. Here’s a Templater template that can be copied:
1<%* 2let qcFileName = await tp.system.prompt("Note Title") 3titleName = qcFileName + " " + tp.date.now("YYYY-MM-DD") 4await tp.file.rename(titleName) 5await tp.file.move("/Notes/" + titleName); 6-%> 7--- 8title: <% qcFileName %> 9date: <% tp.file.creation_date("YYYY-MM-DD HH:mm:ss") %> 10tags: quick_note 11topic: 12--- 13 14<% tp.file.cursor() %>
Here’s the same script with annotations:
The annotations are pretty self explanatory but I definitely struggled with the Eta and Templater documentation. Let me explain in some painful detail.
When Templater runs, it creates a new blank file and then runs the code in the template file. Everything we put in the template file results in some output and changes the empty file Templater just created. It’s all based on Eta templates so there’s a lot more power than just inserting predefined text.
Eta allows single line codes and larger code blocks. Checkout the the the syntax documentation, but I’m using a code block at the top of the template file for all of the basic logic.
The code block uses an “interpolation tag” (an asterisk) to tell the template to execute this and then use the results. Since this block is mostly about moving files, the block outputs a blank line. We will get to this very soon. First, let’s talk about the block.
1let qcFileName = await tp.system.prompt("Note Title")
This neat. We create a variable in the template and tell Obsidian to display a text entry box. The value we put in this box is assigned to the variable
The next line creates another variable that will contain the document title plus the current date. I use this to name the file. Date suffixes are nice to avoid collisions and also add some context when browsing a folder full of files.
await method tells Obsidian to rename the new file but make the user wait to interact until after the file rename is done. Next we tell Obsidian to move the file to a different folder.
1await tp.file.move("/Notes/" + titleName);
I have no idea if the semicolon is required but it works and is predictable so I’m keeping it.
The last line closes the Eta block in a special way that is very important.
As I hinted above, every time an Eta function is executed it outputs something. If the function produces no text then it outputs an empty line. Since this code block is at the top of my template I would end up with a blank line before my YAML front matter, which I don’t want. Fortunately Eta has a lot of control over whitespace and the Templater site has some additional notes. That dash in the Eta block closure removes removes white space after the closure, so that blank line is magically deleted. Neat!
Now we are out of the block and into some basic template stuff. The plain text in the Templater template is inserted as is. Any Eta code is evaluated and also inserted. So this line is inserted as a literal:
But this line is inserted as a combination of literal text PLUS the value of the variable we defined in the block at the top.
1title: <% qcFileName %>
It’s very cool that variables are accessible outside of the block they are created in.
We can also access Obsidian file properties from within the Templater functions. For example, this line grabs the creation date of the current file and formats it as ISO 8601 (the way nature intended).
1date: <% tp.file.creation_date("YYYY-MM-DD HH:mm:ss") %>
When Templater is called from within Obsidian it displays a list of all templates it knows about. There’s a configuration for the plugin where that’s defined.
Templater has two modes when called:
- Create a new file using a template
- Insert text at the current cursor using the output of a template
I’ve pinned both of these commands in Obsidian because I use them all of the time.
There’s a problem though. Templater doesn’t differentiate between templates that I use for new files and templates that I use for snippets. So I have to give myself hints by prefixing the template names with “ff” for “file” and “ss” for “snippet”. I use repeat characters because these are easy to type on iOS and quickly filter the list.
I store all of my templates in a “Meta” directory. There are different template plugins for Obsidian and I want everything organized so I know which templates go with which plugin. I’ve now stopped using most of the other Obsidian plugins for templates.
I had fun with Templater and Obsidian is working better for what I need. That’s a double win. I still find Obsidian to be a little awkward on iOS but Templater helps out there too. Templates are quickly accessible through icons and the command palette so now I can create a new note where I need it with just three taps on iOS. There are some interesting possibilities here and I haven’t even touched Templater system commands or user functions.