Pages
Page frontmatter fields, URL strategy, nested hierarchy, and how parent/child relationships work.
Pages are structural content — About, Documentation, Contact, legal pages. They live in app/content/pages/ and their slug is their filename without .md.
Frontmatter fields
---
title: "Getting Started"
description: "Install and run Blog-Doc."
slug: "getting-started"
order: 1
root: true
status: published
featured_image: /images/hero.webp
---
| Field | Type | Required | Notes |
|---|---|---|---|
title |
string | Yes | Displayed in <title> and navigation |
slug |
string | Yes | Must match the filename |
description |
string | No | Meta description |
parent |
string | No | Slug of the parent page. Cleared when root: true. |
order |
number | No | Navigation sort order. Pages without order sort last. |
root |
boolean | No | If true, served at /{slug} — anchors a URL hierarchy |
featured_image |
string | No | Path or URL to a featured image |
status |
draft | published |
No | Defaults to draft |
URL strategy
Blog-Doc uses slug-as-filename with frontmatter-declared hierarchy:
| Frontmatter | URL produced |
|---|---|
root: true |
/{slug} — e.g. /documentation |
parent: docs |
/{parent-url}/{slug} — e.g. /docs/intro |
| (no parent, no root) | /pages/{slug} — e.g. /pages/portfolio |
Nested page hierarchy
Pages can be organised up to 3 levels deep. Set root: true on the top-level page, then set parent on children and grandchildren:
---
title: "Documentation"
slug: "documentation"
root: true
status: published
---
---
title: "Getting Started"
slug: "getting-started"
parent: "documentation"
status: published
---
---
title: "Installation"
slug: "installation"
parent: "getting-started"
status: published
---
This produces:
/documentation
/documentation/getting-started
/documentation/getting-started/installation
The parent field is the only thing you set. Depth, URL, and breadcrumb data (page.ancestors) are all derived automatically at render time by resolvePageHierarchy().
Rules enforced
- Grandchild pages cannot be parents — max depth is 3.
- Deleting a parent is blocked until its children are re-assigned or removed.
- Setting
root: trueclears any existingparentvalue automatically. - Cycle detection prevents A → B → A parent chains.
In the admin
The Parent Page dropdown in the page editor is pre-populated with eligible parents: root pages and depth-1 pages. Grandchildren are excluded. Setting root: true hides the dropdown entirely.
When you rename a page's slug, any child pages that reference it as a parent are updated automatically — cascade rename is handled server-side.
Template variables
On page.html, the full hierarchy is available at render time:
page.slug // "installation"
page.url // "/documentation/getting-started/installation"
page.depth // 3
page.root // false
page.parent // "getting-started"
page.grandchild // true
page.ancestors // [
// { slug: "documentation", title: "Documentation", url: "/documentation" },
// { slug: "getting-started", title: "Getting Started", url: "/..." }
// ]
Use page.ancestors to build breadcrumbs. Use page.depth or page.grandchild / page.root for conditional layout logic.