Templates

Templates control how your notes look — sidebar, header, footer, and layout.

A template is an HTML file stored in _layouts/. It receives the note's content and frontmatter, then produces a complete page. Your markdown stays clean; the template decides how it's presented.

How templates work

Step 1. Create a file in _layouts/, for example _layouts/my-page.html:

<!DOCTYPE html>
<html>
<head>
  <title>{{ note.Title() }}</title>
</head>
<body>
  <h1>{{ note.Title() }}</h1>
  {{ note.HTMLString() | unsafe }}
</body>
</html>

Step 2. Assign the template in your note's frontmatter:

---
layout: my-page
title: My page
---

Page content in markdown.

The page now uses your template.

Default template layout properties

The built-in default template supports these frontmatter keys — no custom HTML required:

Key Purpose
header: [[Navigation]] Note to use as site header (logo + nav)
footer: [[Footer]] Note to use as site footer
left_sidebar: [TOC, inlinks] Left sidebar widgets
right_sidebar: [outlinks] Right sidebar widgets
content: [selfcontent, magazine] Content blocks rendered in order
magazine_include_property: featured Only show notes that have this frontmatter key
magazine_sort_property: priority Sort magazine cards by this frontmatter key
magazine_include_files: "posts/**/*.md" Glob pattern for magazine notes

Set any sidebar to false to hide it. Set left_sidebar: null (or omit) for no sidebar.

Available sidebar widgets

  • TOC — table of contents (JavaScript-enhanced, tracks active heading)
  • inlinks / Backlinks — notes that link to this note
  • outlinks — links from this note
  • [[Title]] — embed another note by title
  • path/to/file.md — embed a note by file path

Content block types

  • selfcontent (or self) — this note's own article with <h1> + body
  • magazine — multi-tier card layout (featured, grid, list)
  • [[Title]] — embed a note by title
  • path/to/file.md — embed a note by file path

Magazine layout

The magazine layout shows child notes as cards in three tiers:

Tier Position Visual
Featured First note Large full-width card
Grid Notes 2–5 Smaller cards, 4-column
List Notes 6+ Minimal, vertical list

Activate it on an index note:

---
content:
  - magazine
magazine_sort_property: priority
magazine_include_files: "posts/**/*.md"
---

The header and footer are ordinary notes. The default template reads:

  • Logo — the first image in the header note
  • Navigation — the first list in the header note
  • Footer columns — a nested list (top-level items become column headings)

Example header note _nav.md:

---
title: Navigation
---

![Logo](/logo.png)

- [Home](/)
- [Docs](/docs)
  - [Getting started](/docs/start)
  - [Templates](/docs/templates)
- [About](/about)

Then reference it from any note:

header: [[_nav]]

Store navigation in a separate markdown file so you can update it without touching the template:

---
title: Sidebar
---

### Section 1

- [Page 1](/docs/page-1)
- [Page 2](/docs/page-2)

Reference it as a sidebar widget:

left_sidebar:
  - _sidebar.md

What's available in a custom template

The Jet template engine provides these variables:

{{ note.Title() }}          — title from frontmatter
{{ note.HTMLString() }}     — full content as HTML
{{ note.Permalink() }}      — page URL
{{ note.ReadingTime() }}    — reading time in minutes
{{ note.CreatedAt() }}      — creation date
{{ note.M().GetString("author", "Unknown") }}  — frontmatter field

Access other notes via nvs:

{{ sidebar := nvs.ByPath("/_sidebar.md") }}
{{ if sidebar }}
  {{ sidebar.HTMLString() | unsafe }}
{{ end }}

Load assets:

<link rel="stylesheet" href="{{ asset("style.css") }}">

Splitting content into sections

PartialRenderer breaks a markdown note into logical blocks — useful for landing pages, FAQs, and card grids:

{{ intro := note.PartialRenderer().Introduce() }}
<p class="lead">{{ intro.ContentHTML | unsafe }}</p>

{{ range i, s := note.PartialRenderer().Sections(3) }}
  <details>
    <summary>{{ s.TitleHTML | unsafe }}</summary>
    {{ s.ContentHTML | unsafe }}
  </details>
{{ end }}
  • Introduce() — content before the first heading
  • Sections(level) — sections under headings of a given level
  • Section("Title") — a specific section by heading text

Organizing multiple templates

For sites with shared header, footer, and styles, use a blocks.html file:

_layouts/
└── my-theme/
    ├── blocks.html   — shared header, footer, wrapper
    ├── page.html     — standard article page
    └── landing.html  — landing page

blocks.html defines reusable blocks; page.html imports and uses them:

{{ import "blocks" }}

{{ yield main_layout() content }}
  <article class="prose">
    <h1>{{ note.Title() }}</h1>
    {{ note.HTMLString() | unsafe }}
  </article>
{{ end }}

Jet template syntax

Templates use the Jet engine:

{{ variable }}                        — output
{{ if condition }}...{{ end }}        — conditional
{{ range i, item := list }}...{{ end }} — loop (always capture both index and value)
{{ block name() }}...{{ end }}        — define a block
{{ yield name() }}                    — call a block
{{ include "path" data }}             — include a partial
{{ value | unsafe }}                  — output HTML without escaping

Two Jet rules to remember:

  1. Block parameters need default values or named arguments won't bind: {{ block card(title="", body="") }}
  2. content is a reserved keyword — don't use it as a parameter name

Querying notes in templates

nvs.ByGlob() selects notes by path pattern and supports sorting and pagination:

{{ range i, post := nvs.ByGlob("blog/*.md").SortBy("CreatedAt").Desc().Limit(5).All() }}
  <a href="{{ post.Permalink() }}">{{ post.Title() }}</a>
{{ end }}

Methods: SortBy("Title"), SortBy("CreatedAt"), SortByMeta("order"), .Desc(), .Asc(), .Limit(n), .Offset(n), .All(), .First(), .Last()