Frontmatter
Field definitions, merge strategies, reserved keys, and update behavior for literature note frontmatter.
This page documents how ZotLit manages YAML frontmatter on literature notes: the fields ZotLit owns, the managed fields a profile declares, merge strategies, and the behavior during note updates.
For step-by-step configuration, see Configure frontmatter properties.
System-managed fields
ZotLit writes these keys itself. Managed frontmatter cannot target them.
| Field | Source | Description |
|---|---|---|
zotero-key | zt.indexedKey | Literature note identity: KEY for My Library or KEYgGROUPID for group libraries |
zotlit-profile | The profile that ran the operation | The profile that produced the note, as its name and 12-character ID. Absent when the Default profile produced it |
zotlit-csl | The profile's citation style | CSL ID the note's citations and references render with. Absent when the profile binds no style |
User-configured fields
Managed frontmatter is the ordered list of entries under the frontmatter key of a profile document's manifest. Each entry produces one property, or several at once.
Edit the list in the Properties tab of the Template Workbench. On the Templates and properties row at Settings > ZotLit, select Edit profile.
Each entry has an optional key, an optional merge strategy, and exactly one value member. The member name declares the language, so an entry carries no separate language setting:
| Member | Required | Description |
|---|---|---|
key | No | The YAML property name written to the note. Omit it for a spread entry |
expr | One of the three | A Liquid value expression |
value | One of the three | A JSON-e template |
js | One of the three | A JavaScript expression. Inert while the JavaScript Templates gate is off |
merge | No | How the entry applies when the note is updated. replace (the default), append, or keep |
One entry, with key, value, and merge written out:
frontmatter:
- key: citekey
value: { "$eval": "zt.citationKey" }
merge: replaceThe Value format control names the value members in the editor: Zotero field · Liquid writes an expr, Rule · JSON-e writes a value, and Fixed text writes a value that holds the text as written. A js entry is edited in the Source view.
A value can be written in any YAML form, such as the block mapping under Spread entries. When you edit a value in the editor, the Workbench saves it as one line of JSON in the profile document. Other entries keep the form they were written in.
Spread entries
An entry with no key is a spread entry. Its value or js member evaluates to a mapping, and every produced key lands as a field at that entry's position, under the entry's one merge strategy.
frontmatter:
- value:
title: { "$eval": "zt.title" }
"zotero/${zt.itemType}": trueInside a spread mapping, a false $if omits that key and leaves the note's value alone. ${} interpolation builds a key name. A top-level $let shares one calculation across every produced field. A $if that is false at the root of the entry contributes an empty patch, so the entry writes nothing.
expr always produces a single value, so a spread entry cannot use it: a keyless expr entry fails validation. A spread entry whose result is not a mapping, or that produces a reserved or empty key, refuses the whole operation. Diagnostics name the entry by its 1-based position in the list.
In the editor, Add several properties from one rule adds a spread entry. Its row reads Several properties and lists the properties it produced.
Default fields
Out of the box, the Default profile declares four entries. Its document reads:
frontmatter:
- key: title
expr: zt.title
merge: replace
- key: related
expr: zt.relatedItems | note_links
merge: replace
- key: collections
expr: zt.collections | collection_paths
merge: replace
- key: citekey
value: { "$eval": "zt.citationKey" }
merge: replaceThe citekey entry writes citekey: null when the Zotero item has no citation key. All four are ordinary managed frontmatter. You can edit or remove them like any other entry.
Liquid expressions
An expr member is one value expression, not a template block. It uses the same filter vocabulary as templates (see Template syntax) and returns typed values (arrays, numbers, strings), not rendered text.
zt.title -> "My Paper Title"
zt.authors | join: ", " -> "Jane Smith, Bob Jones"
zt.tags | map: "name" -> ["methodology", "open access"]
zt.tags | obsidian_tag -> ["methodology", "open_access"]
zt.collections | collection_paths -> ["Project/Reading", "Research"]
zt.date.year -> 2023JavaScript expressions
A js member holds a JavaScript expression. It requires the JavaScript Templates gate. Any valid JavaScript expression works, with the full note context available as zt:
zt.authors.map(c => c.fullName) -> ["Jane Smith", "Bob Jones"]
zt.tags.map(t => t.name) -> ["methodology", "review"]
zt.date?.year -> 2023The return value is serialized to YAML automatically.
Merge strategies
Each entry selects one merge strategy, set with When the note is updated in the editor:
| Strategy | Editor option | Behavior |
|---|---|---|
replace | Replace the existing value | Writes the generated value unconditionally. The existing value is overwritten |
append | Add to the existing list | Appends new generated values not already present to the existing array. Manual additions remain. If existing is blank, writes the generated value. Both the existing and generated values must be arrays; when either side is not an array (and existing is not blank), the field is left unchanged |
keep | Keep the existing value | Writes the generated value only when the existing field is blank. Once the field has a value, it is never overwritten |
Absent values
Only a value entry can produce absence. A JSON-e template that renders nothing at its root, such as one whose top-level $if is false, contributes no value at all.
Absence is the one way to delete a managed key. Under Replace the existing value an absent value deletes the existing key; under Add to the existing list and Keep the existing value it leaves the key as it stands.
What counts as blank
A value is blank (and treated as absent by Add to the existing list and Keep the existing value) when it is:
nullor undefined- An empty string (
"") - An empty array (
[]) - An empty plain object (
{})
Undefined expressions
When a Liquid or JavaScript expression returns undefined, the field is untouched regardless of the merge strategy. Only defined values (including explicit null) reach the merge logic.
Append arrays detail
The Add to the existing list strategy preserves existing items verbatim (including any duplicates already present) and appends only generated items not already in the array.
Reserved keys
ZotLit refuses a static key, or a key a spread entry produces, from this set:
| Key | What it protects |
|---|---|
zotero-key | Literature note identity. The item key, with the group library ID appended for group libraries |
zotlit-profile | The profile stamp that records which profile produced the note. Absence selects the Default profile |
zotero-note-key | Identity of an imported child note. Kept apart from zotero-key so the note never registers as a literature note |
zotero-lastmod | The source child note's dateModified, which batch re-import reads to skip unchanged notes |
zotlit-csl | The CSL style ID one note's citations and references render with, set from the profile's citation style |
A profile document that declares a reserved or duplicate static key fails to load, and ZotLit excludes it from the profiles it offers instead of repairing it. See Profiles.
Validation
The Properties tab marks the row that failed with Problem, and the row shows what went wrong in place of a value. The full explanation, which names the failing key and the entry's position, is in the Problems area of the editor.
Validation has three stages:
- The document. A reserved static key, a duplicate static key, or a keyless
exprentry stops the document from loading. - The expression. An
exprorjsmember that fails to compile, or avaluethat cannot evaluate, is a problem the editor names by key and entry position. - The write. A
valueorjsentry whose result falls outside the frontmatter value domain, a spread entry that produces no mapping, or a produced key that is reserved or empty refuses the whole operation.
A refused operation writes nothing. ZotLit never applies a partial patch to a note. A js entry that is inert while the gate is off refuses by key, or by entry position when the entry is keyless.
Merge on update
When you run ZotLit: Update literature note or ZotLit: Update literature note metadata, ZotLit refreshes frontmatter:
- Managed entries are re-evaluated in the order they appear in the
frontmatterlist of the profile document and applied with their merge strategy. - System identity (
zotero-key) is refreshed. - Unmanaged keys: any frontmatter key not in the managed set (e.g.
aliases,tags,cssclasses) is preserved. ZotLit never touches keys it does not own.
ZotLit: Overwrite literature note replaces the note body but applies the same frontmatter behavior: managed keys are refreshed and unmanaged keys are kept.
ZotLit writes frontmatter through Obsidian's processFrontMatter API, which preserves key ordering and formatting for unmanaged keys. On a newly created note, managed fields are written in the order they appear in the profile document's frontmatter list, followed by system fields. An entry later in the list wins the value, because each entry is applied over the value the entries before it produced.
Removing citekey or another entry from the frontmatter list does not delete existing values. Those values become unmanaged and remain in the note.
JavaScript Templates gate
The JavaScript Templates gate controls whether js entries run:
- Gate off:
jsentries are inert. They are not evaluated. Any operation that depends on an inert entry surfaces an error naming those keys.exprandvalueentries are unaffected. - Gate on:
expr,value, andjsentries evaluate normally.
The gate never reinterprets a member. An expr entry always evaluates as Liquid, and a js entry always evaluates as JavaScript.
A spread entry that uses js is refused by its entry position while the gate is off, because its keys cannot be known until it runs.
Inert entries block note operations
When any frontmatter entry uses the js member and the gate is off,
literature note creation and update fail with an error listing the inert keys.
Either enable the gate or rewrite those entries as expr or value. See
Enable JavaScript templates.
See also
Profile document
The manifest keys, note source, and Annotation Section of a profile document.
Configure frontmatter properties
Add, edit, and reorder frontmatter fields step by step.
Profiles
How a profile selects a note's template, folder, and citation style.
Data reference
Properties available on zt for each template type.
JavaScript templates
What the gate enables, risks, and disabled behavior.
Last updated on