Posts are dated, categorised entries — articles, blog posts, news. They live in app/content/posts/ and are always served at /blog/{slug}.

Frontmatter fields

---
title: "Hello World"
description: "My first post."
slug: "hello-world"
date: 2025-06-01
category: "Web Development"
tags:
    - "javascript"
    - "node"
related_posts:
    - "another-post"
    - "yet-another-post"
status: published
featured_image: /images/post-cover.webp
author: LebCit
---
Field Type Required Notes
title string Yes
slug string Yes Must match the filename
description string No Meta description and search excerpt source
date string No YYYY-MM-DD. Auto-set to today on create if omitted
category string No A single category string — powers /categories/ routes
tags array No Array of tag strings — powers /tags/ routes
related_posts array No Ordered array of slugs (max 4)
featured_image string No Path or URL
status draft | published No Defaults to draft
author string No

The root and order fields do not apply to posts.

Category and tags

category is a single string. tags is a YAML block array. Both power taxonomy routes automatically — no configuration needed.

In templates, two versions of each are available:

Variable Example Use for
post.category "web-development" URLs — href="/categories/{{ post.category }}"
post.categoryName "Web Development" Display — {{ post.categoryName }}
post.tags ["javascript", "node"] Raw strings for display and linking

Taxonomy slugs are derived by lowercasing, trimming, removing special characters, and replacing spaces/underscores with hyphens — "Web Development"web-development.

Related posts

An ordered array of related posts chosen by the author in the post editor (max 4). The array is null when no related posts have been set for this post. Items expose the following properties:

Variable Type Notes
post.relatedPosts object[] | null null when no related posts are set
post.relatedPosts[n].slug string URL identifier
post.relatedPosts[n].title string
post.relatedPosts[n].description string Empty string if not set
post.relatedPosts[n].date string YYYY-MM-DD; empty string if not set
post.relatedPosts[n].category string | null Raw category string (not slugified); null if unset
post.relatedPosts[n].featured_image string | null

Order reflects the author's editorial intent (the sequence in which items were selected in the editor). Stale slugs pointing to deleted or re-drafted posts are automatically dropped at render time — the array will be shorter than the stored list when this occurs.

Navigation

Adjacent posts, prevPost and nextPost, for building post navigation. Each is either an object or null.

Variable Type Notes
prevPost object | null The chronologically older post; null if this is first
prevPost.slug string
prevPost.title string
prevPost.featured_image string | null
nextPost object | null The chronologically newer post; null if this is last
nextPost.slug string
nextPost.title string
nextPost.featured_image string | null

Taxonomy routes

When a theme declares "blog" in its features array and includes a taxonomy.html template, Blog-Doc automatically activates:

URL pattern What it shows
/categories/{category} Posts in that category (page 1)
/categories/{category}/page/{n} Paginated continuation
/tags/{tag} Posts with that tag (page 1)
/tags/{tag}/page/{n} Paginated continuation

These pages are generated automatically during a static build into _site/categories/ and _site/tags/.

Pagination

The blog index and taxonomy pages are paginated according to the postsPerPage setting (default 5, configurable in Settings → General).

The pagination object is available on blog.html, taxonomy.html, and home.html:

pagination.currentPage // 2
pagination.totalPages // 4
pagination.prevPage // 1  (null on page 1)
pagination.nextPage // 3  (null on last page)
pagination.hasPrev // true
pagination.hasNext // true

Sorting

The list endpoint sorts posts newest-first by date. Posts without a date sort to the end. In the admin list view, the same order applies.

Excerpt generation

Blog-Doc generates a plain-text excerpt automatically — no excerpt frontmatter needed. The excerpt is the first 200 characters of the Markdown body with code, images, and emoji stripped. It's available as post.excerpt in blog.html, taxonomy.html, and home.html.

Search index

Every published post is included in search.json with these fields:

{
    "type": "post",
    "title": "Hello World",
    "description": "My first post.",
    "url": "https://example.com/blog/hello-world/",
    "body": "First 300 chars of plain text...",
    "date": "2025-06-01",
    "category": "web-development",
    "tags": ["javascript", "node"]
}

The index is generated at server start and on every build, served at /data/search.json.