Skip to content

Guide: Templating

Johann Richard edited this page Nov 2, 2025 · 8 revisions

Readwise Mirror uses the powerful Nunjucks templating engine to give you full control over the content and structure of your notes. This guide covers the available templates, variables, and filters that you can use to customize your sync.

The Building Blocks of a Template

The templating system works by combining templates, variables, and filters:

1. Templates: The Blueprint

  • Function: A template defines the structure and layout of the Obsidian notes this plugin generates from each Readwise item that is synced. It contains a mix of static text (like headings) and dynamic placeholders for your Readwise data. The plugin uses three templates which are combined to create a single note.

  • Application: The plugin builds each note in a specific order:

    1. First, the Frontmatter Template is processed to create the note's metadata.
    2. Next, the Header Template is processed to create the top section of the note's body.
    3. Finally, the plugin loops through every highlight and applies the Highlight Template once for each, appending the results in order.

2. Variables: The Data Placeholders

  • Function: A variable is a placeholder that represents a specific piece of data from your Readwise library, such as a book's title or the text of a highlight.

  • Application: You insert a variable into a template using double curly braces: {{ variable_name }}. During a sync, the plugin replaces this placeholder with the actual data.

    • Example: The variable {{ title }} is replaced by the book's title, such as The Psychology of Money.

3. Filters: The Data Formatters

  • Function: A filter is a special function that modifies the content of a variable before it is placed in the final note. Filters are used for formatting, such as changing a date's appearance or ensuring text is correctly quoted. They do not change your original data in Readwise, only how it is displayed in Obsidian.

  • Application: You apply a filter to a variable using the pipe symbol (|). This "pipes" the variable's content through the filter to transform it.

    • Example: To format a timestamp into a simple date:
      • Variable: {{ last_highlight_at }}
      • Raw Data: 2024-02-18T10:00:00Z
      • Template Code: {{ last_highlight_at | date("YYYY-MM-DD") }}
      • Final Output: 2024-02-18

Template Types

You can customize three distinct parts of each note, which are combined to create the final file.

  1. Frontmatter Template: Controls the note's metadata (also called "properties" in Obsidian). This section is at the very top of the file and must be valid YAML.
  2. Header Template: Defines the top section of the note's body. This is the perfect place for displaying cover images, titles, authors, and other metadata you want to see in the note content itself.
  3. Highlight Template: Formats each individual highlight and its associated notes. This template is rendered once for every highlight in the document and appended in order.

Template Variables

Variables are placeholders for data from Readwise. You can insert them into your templates using {{ variable_name }}.

Document Variables

These variables are available in the Frontmatter and Header templates.

Frontmatter and Header Metadata

Variable Description Example
id The unique ID for the document 12345
title The original title of the document "The Psychology of Money"
sanitized_title A filesystem-safe version of the title "The-Psychology-of-Money"
author The author's name(s) as an array ['Morgan Housel']
authorStr The author's name formatted with wiki links "[[Morgan Housel]]"
category The content type "books"
document_note Your note on the document itself "A great collection of..."
summary The document's official summary "Timeless lessons on wealth..."
num_highlights The total number of highlights 42

URLs

Variable Description Example
highlights_url The URL for the item in Readwise "https://readwise.io/..."
source_url The URL of the original content "https://www.collaborativefund.com/..."
unique_url The Reader URL (if available) "https://readwise.io/reader/..."
cover_image_url The URL for the cover image "https://images-na.ssl-images-amazon.com/..."

Tags

Variable Description Example
tags Document tags, including the # "#finance, #psychology"
tags_nohash Document tags without the # "'finance', 'psychology'"
highlight_tags All highlight tags, including the # "#important, #quote"
hl_tags_nohash All highlight tags without the # "'important', 'quote'"

Timestamps

Variable Description Example
created The date the item was created "2023-01-15T12:00:00Z"
updated The date the item was last updated "2024-02-20T18:30:00Z"
last_highlight_at The date of the most recent highlight "2024-02-18T10:00:00Z"

Highlight Template Variables

These variables are available only inside the Highlight Template.

Content

Variable Description Example
text The highlighted text "The highest form of wealth is..."
note Your personal annotation on the highlight "This is a key insight."
color The color of the highlight "yellow"

Location & Links

Variable Description Example
location The position reference (e.g., page) "Page 42"
location_url A direct link to the location (e.g., Kindle). Empty (undefined/empty if ASIN and/or location are not available) "kindle://..."
url A direct link to the highlight on Readwise "https://readwise.io/open/..."

Highlight Metadata

Variable Description Example
id The unique ID for the highlight "98765"
tags Tags associated with the highlight "#important, #todo"
updated_at The timestamp of the last update "2024-02-20T18:30:00Z"

Custom Filters

Filters are functions you can apply to variables to transform their output. The syntax is {{ variable | filter }}.

Filter Description Example
fme Escapes frontmatter, deals with multi-line text, quotes, and other special characters. Use this as the last filter to escape (all) variables in your frontmatter templates or in Atomizer blocks {{ text | fme }}
bq Adds blockquote markers (>) to each line of text. {{ text | bq }}
is_qa Returns true if a note contains the .qa action tag. {% if note | is_qa %}
qa Converts a .qa note into a Q&A format. {{ note | qa }}
date Formats a timestamp using Moment.js syntax. {{ created | date("YYYY-MM-DD") }}

Frontmatter Escape Filter (fme)

This filter escapes the input and ensures valid frontmatter is generated. It works for both single-line and multi-line values. Use it in the frontmatter templates and in atomize blocks in the highlights template.

The following will escape a single-line value:

---
title: {{ title | fme }} 
---

The following escapes the note and highlight into a properly formatted frontmatter property which can be used in an atomize block:

---
highlight: {{ text | fme }} 
note: {{ note | fme }}
---

N.B.: This also works for JSON when using the dump() filter: json: {{ json | dump(4) | fme(true) }}, will dump a variable json into a text, and then escape it into a valid multiline block.

Blockquote Filter (bq)

This filter correctly formats multi-paragraph highlights as a single blockquote in Markdown, preserving line breaks.

Template:

> [!quote]
> {{ text | bq }}
> — [[{{ authorStr}}]] on page {{ location }}

Result:

> [!quote]
> This is the first paragraph of the highlight.
>
> And this is the second paragraph.
> [[Morgan Housel]] on page 42

Q&A Filter (is_qa and qa)

These filters properly render flashcards created with the .qa action tag in Readwise.

Template:

{% if note and note | is_qa %}
**Question:**
{{ note | qa }}

**Original Highlight:**
> {{ text }}
{% else %}
{{ text }}
{% if note %}
> **Note:** {{ note }}
{% endif %}
{% endif %}
---

Date Filter (date)

The date filter uses moment.js for robust date formatting.

Template: Date Highlighted: {{ last_highlight_at | date("MMMM Do, YYYY") }}

Result: Date Highlighted: February 18th, 2024

Author parser

The plugin will split the authors from Readwise into an array of individual author names which can then be used in your templates as you wish.

The author field with the following value John Doe, Jane Smith, and Homer Simpson would be split into an array consiting of [ 'John Doe', 'Jane Smith', 'Homer Simpson']. You can then use other nunjucks filters, including join(), to rebuild a string in your template.

For example the following frontmatter and heading template

---
author: [ {{ author | join(', ') }} ]
---

...

Author: [[{{ author | join(']], [[') }}]]

Would render as

---
author: [ 'John Doe', 'Jane Smith', 'Homer Simpson' ]
---

...

Author: [[John Doe]], [[Jane Smith]], [[Homer Simpson]]

Note

This will not work for authors that are stored in Readwise in the scientific notation (Last, First). If you end up having such cases stored in your Readwise library, it is best to manually correct them at the source or in Readwise and sync again to Obsidian.

Author Name Normalization Filter (normalize_author)

This filter helps standardize author names by removing common honorifics and titles, making them more consistent for linking and organization.

What it does

  • Removes common academic and courtesy titles like:
    • Academic: Dr, Professor, Prof
    • Courtesy: Mr, Mrs, Ms, Miss, Mx
    • Honorary: Sir, Lord, Lady, Dame
  • Cleans up extra whitespace
  • Works with both single authors and arrays of authors

Usage

{{ author | normalize_author }}

Examples

{{ "Dr. Jane Doe" | normalize_author }}
→ "Jane Doe"

{{ "Prof. John Smith" | normalize_author }}
→ "John Smith"

{{ ["Mrs. Alice Brown", "Dr. Bob Wilson"] | normalize_author }}
→ ["Alice Brown", "Bob Wilson"]

Practical Template Examples

Here is one examples to get you started.

Example 1: Minimalist Setup

This setup is clean and simple, focusing on basic metadata and clear presentation, using the qa filter to format Readies Q&As.

Frontmatter Template:

title: {{ title }}
tags: [readwise, {{ category }}]
author: "{{ author | join(', ') }}"
source: {{ source_url }}
uri: {{ highlights_url }}

Header Template:

# [[{{ title }}]]
*by [[{{ author | join(']], [[') }}]]*

---
# Highlights

Highlight Template:

```nunjucks
{% if note and note | is_qa %}
**Question:**
{{ note | qa }}

**Original Highlight:**
> {{ text }}
{% else %}
{{ text }}
{% if note %}
> **Note:** {{ note }}
{% endif %}
{% endif %}
***

Author Normalization Template Example

---
author: {{ author | normalize_author | join(', ') }}
---

Authors: {% for name in author | normalize_author -%}
[[{{ name }}]]{% if not loop.last %}, {% endif %}
{%- endfor %}

Template limitations

The templating is based on the nunjucks templating library and thus shares its limitations.