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: true clears any existing parent value 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.