Examples

Pages

Pages are Markdown files that become HTML pages on your site. This page covers how to write content — front matter fields, Markdown syntax, and file organization. For how these fields are accessed in templates, see the Data Model.

Basic Structure

+++
title = "My Page"
date = "2024-01-15"
+++

Your content in **Markdown**.

The +++ block is TOML front matter. YAML (--- delimiters) and JSON (a top-level {...} object at the start of the file) are also supported. Content below becomes HTML.

---
title: "My Page"
date: "2024-01-15"
---

Your content in **Markdown**.
{
  "title": "My Page",
  "date": "2024-01-15"
}

Your content in **Markdown**.

For JSON, the first balanced {...} at the very start of the file is the front matter — no fence is needed. The file must begin with { (no leading whitespace).

Front Matter

Required

Field Type Description
title string Page title

Common Fields

Field Type Default Description
date string Publication date (YYYY-MM-DD)
description string SEO description
draft bool false Exclude from production builds
template string "page" Template to use
weight int 0 Sort order (lower = first)
image string Featured image for social sharing
tags array [] Tag taxonomy terms
categories array [] Category taxonomy terms

All Fields

Field Type Description
updated string Last updated date
slug string Custom URL slug
path string Custom URL path
aliases array Redirect URLs to this page
authors array Author names
toc bool Show table of contents
in_search_index bool Include in search
in_sitemap bool Include in sitemap
insert_anchor_links bool Add heading anchors
redirect_to string Redirect page to this URL
render bool Render page to output (default: true)
expires date Auto-exclude after this date
series string Series name for grouping
series_weight int Sort order within series
extra table Custom metadata

Examples

Blog Post

+++
title = "Getting Started with Crystal"
date = "2024-01-15"
description = "Learn Crystal programming basics"
tags = ["crystal", "tutorial"]
authors = ["Alice Smith"]
image = "/images/crystal-guide.png"
+++

Crystal is a fast, compiled language...

Draft

+++
title = "Work in Progress"
draft = true
+++

Not visible in production.

Build with drafts: hwaro build --drafts

Expiring Content

+++
title = "Limited Time Offer"
expires = 2025-12-31
+++

Automatically excluded from builds after the expiry date.

Build with expired content: hwaro build --include-expired

Pages expiring within 7 days generate a build warning.

Future-Dated Content

Pages with a date in the future are automatically excluded from builds. This is useful for scheduling content.

+++
title = "Coming Soon"
date = 2099-01-01
+++

Published only after the date arrives.

Build with future content: hwaro build --include-future

Series Post

+++
title = "Part 1: Introduction"
series = "Crystal Tutorial"
series_weight = 1
+++

First part of the series.

In templates, access page.series, page.series_index, and page.series_pages.

Custom Template

+++
title = "Landing Page"
template = "landing"
+++

Uses `templates/landing.html` instead of `page.html`.

Weighted Order

+++
title = "Introduction"
weight = 1
+++
+++
title = "Getting Started"
weight = 2
+++

Lower weight appears first.

URL Aliases

+++
title = "New Page"
aliases = ["/old-url/", "/another-old-url/"]
+++

Redirects from old URLs to this page.

Custom Metadata

+++
title = "Product Review"

[extra]
rating = 4.5
featured = true
pros = ["Fast", "Reliable"]
+++

Access in templates: {{ page.extra.rating }}

Full Front Matter Reference

All available fields in one block. Copy and remove what you don't need.

+++
title = "Page Title"
date = "2024-01-15"
updated = "2024-02-01"
description = "SEO description"
draft = false
template = "page"
weight = 0
slug = "custom-slug"
path = "custom/path"
aliases = ["/old-url/"]
image = "/images/cover.png"
tags = ["tag1", "tag2"]
categories = ["category1"]
authors = ["Author Name"]
toc = true
in_search_index = true
in_sitemap = true
insert_anchor_links = true
render = true
redirect_to = ""
expires = 2025-12-31
series = "Series Name"
series_weight = 1

[extra]
custom_field = "value"
+++

Content Summary

Use <!-- more --> to define a summary:

+++
title = "Long Article"
+++

This is the summary shown in listings.

<!-- more -->

The full article continues here...

Markdown Syntax

Text

**bold** and *italic*
`inline code`
[link](https://example.com)
![image](/img.jpg)

Lists

- Unordered
- Items

1. Ordered
2. Items

Code Blocks

```javascript
console.log("Hello");
```

Tables

<table>
<thead>
<tr>
<th>Header</th>
<th>Header</th>
</tr>
</thead>
<tbody>
<tr>
<td>Cell</td>
<td>Cell</td>
</tr>
</tbody>
</table>

Table cells support inline Markdown: bold, italic, code spans, links, images, and strikethrough.

<table>
<thead>
<tr>
<th>Feature</th>
<th>Example</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bold</td>
<td><strong>important</strong></td>
</tr>
<tr>
<td>Italic</td>
<td><em>emphasis</em></td>
</tr>
<tr>
<td>Code</td>
<td><code>config.toml</code></td>
</tr>
<tr>
<td>Link</td>
<td><a href="https://example.com">Hwaro</a></td>
</tr>
<tr>
<td>Image</td>
<td><img src="/img/logo.png" alt="logo"></td>
</tr>
<tr>
<td>Strikethrough</td>
<td><del>deprecated</del></td>
</tr>
</tbody>
</table>

Use @/ to link to other content pages by their source path. Hwaro resolves these to the correct output URL at build time.

[Read the post](@/blog/my-post.md)
[About section](@/about/_index.md)
[With anchor](@/blog/my-post.md#introduction)

This is useful because you don't need to know the final URL — Hwaro calculates it from the content path. If the target page doesn't exist, the link is left unchanged and a warning is logged during build.

Syntax Resolved URL
@/blog/post.md /blog/post/
@/blog/_index.md /blog/
@/blog/post.md#section /blog/post/#section

Blockquotes

> Quote text

Admonitions

GitHub-style alert blocks render as styled callouts. Recognised types: NOTE, TIP, IMPORTANT, WARNING, CAUTION.

> [!NOTE]
> Pay attention to this paragraph.

> [!WARNING]
>
> Body can also live in its own paragraph.

The output is a <div class="admonition admonition-{type}"> with a title paragraph (<p class="admonition-title">) followed by the body. Style it from your CSS — Hwaro emits semantic markup only.

Disable by setting admonitions = false under [markdown] in config.toml.

Limitations: matching is type-case-sensitive ([!NOTE] only, not [!note]), and a nested blockquote inside an admonition body closes the outer admonition early. There is no inline escape — backslash-escaping (\[!NOTE\]) renders the same characters and still triggers the admonition, so disable the feature if you need to render the literal token.

Custom Heading IDs

Append {#custom-id} to a heading line to override the auto-generated slug. Useful when you want stable anchor URLs that don't break on title edits.

## Installation Guide {#install}

Renders as <h2 id="install">Installation Guide</h2>. The TOC and any [link](#install) will use the custom id.

Allowed id characters: letters, digits, _, -, :. The id must start with a letter. CommonMark allows up to 3 leading spaces before an ATX heading; deeper indentation makes the line a code block, in which case {#id} is not applied.

Disable by setting heading_ids = false under [markdown] in config.toml.

Custom heading IDs require markdown.safe = false. Under safe mode the {#id} syntax is stripped from the rendered output and no id is applied — use raw HTML headings if you need both safe mode and explicit ids. Writing the same {#id} twice in one page produces duplicate id attributes; the first anchor wins.

Definition Lists

<dl>
<dt>Term</dt>
<dd>Definition body</dd>
<dt>Another term</dt>
<dd>Definition with <strong>bold</strong>, <em>italic</em>, <code>code</code>, <a href="https://example.com">a link</a>, and <del>strikethrough</del></dd>
<dd>A second definition for the same term</dd>
</dl>

Inline Markdown works inside both terms and definitions. Raw HTML is escaped for safety.

Asset Colocation

You can keep related assets (images, PDFs, etc.) in the same directory as your content file. This is known as a Page Bundle.

To use this feature, rename your markdown file to index.md (for regular pages) or _index.md (for section pages) and place it in a directory named after your page.

Example Structure:

content/
└── blog/
    ├── my-trip/
    │   ├── index.md        <-- The page content
    │   ├── photo.jpg       <-- Asset
    │   └── data.json       <-- Asset
    └── _index.md

Hwaro will copy all non-markdown files from the page bundle directory to the output directory, maintaining the relative path.

In your markdown, you can link to these assets using relative paths:

![My Trip Photo](photo.jpg)

[Download Data](data.json)

Accessing Assets in Templates

You can access the list of colocated assets in your templates using page.assets. This returns an array of relative paths to the files.

{% for asset in page.assets %}
  {% if asset is matching("[.](jpg|png)$") %}
    <img src="{{ get_url(path=asset) }}" alt="Gallery Image">
  {% endif %}
{% endfor %}

URL Mapping

File URL
content/index.md /
content/about.md /about/
content/blog/post.md /blog/post/

See Also