PageWeave / Docs

Overview

Markdown

Liquid Templating

All HTML in PageWeave supports Liquid. Use it for dynamic content, loops, conditionals, and data table access.

Variables

Global Drops

  • site.name — Website name

  • site.subdomain — Website subdomain

  • site.domain — Custom domain (if set)

  • site.language — BCP 47 language tag (from website setting)

  • site.indexable — Search engine indexing enabled

  • site.tables.{slug} — Data table rows

  • page.title — Page title

  • page.path — Page URL path

  • page.language — Page-level language override (BCP 47 tag)

  • page.indexable — Whether search engines index this page

Template Pages

When a page is associated with a table, the row variable is available. Every placeholder in the page's path maps verbatim to a row data field (uniform field semantics):

  • row.id — Row UUID
  • row.slug — the slug data field's value (alias for row.data.slug). You supply it in row data — no auto-generation.
  • row.created_at — Creation timestamp
  • row.updated_at — Last update timestamp
  • row.url — Full URL to this row's page, in its collapsed canonical form (default-locale segments omitted, e.g. /docs/seo not /docs/en/seo)
  • row.{field} — Any table field
  • row.{field}.{nested} — Nested data

Syntax

{{ site.name }}
{% if site.indexable %}
  <meta name="robots" content="index, follow">
{% endif %}
{% for i in (1..3) %}{{ i }}{% endfor %}
{% assign x = "value" %}

Filters

Standard Liquid filters: upcase, downcase, date, truncate, times, divided_by, append, prepend, strip_html.

Table filters:

  • where: "field", "value" — Filter rows
  • sort_by: "field", "desc" — Sort rows (numeric fields sort numerically)

Filters chain on {% assign %} — assign the filtered collection to a variable first, then loop over it:

{% assign posts = site.tables.blog_posts | where: "published", "true" | sort_by: "created_at", "desc" %}
{% for post in posts %}
  <h2>{{ post.title }}</h2>
{% endfor %}

Note: Liquid ignores filter chains written directly inside a {% for %} tag ({% for post in site.tables.blog_posts | where: ... %} renders every row, unfiltered, with no error). Page validation flags this pattern with a warning pointing to the assign-first form above.

Markdown Rendering

The markdownify filter renders markdown as HTML — safe by default (literal raw HTML in the source is neutralized), with the full GFM set: tables, task lists, strikethrough, autolinks, and soft line breaks.

<div class="prose">{{ row.content | markdownify }}</div>

Use it to render markdown stored in data tables (e.g. documentation bodies, blog post content) inside template pages.

Data Tables

Basic Loop

{% for post in site.tables.blog_posts limit: 10 %}
  <h2>{{ post.title }}</h2>
  <p>{{ post.excerpt | truncate: 150 }}</p>
{% endfor %}

Pagination

{% paginate site.tables.blog_posts by 10 order_by: "created_at" order_dir: "desc" where: "published:true" %}
  {% for post in site.tables.blog_posts %}
    {{ post.title }}
  {% endfor %}
  {% if paginate.next %}
    <a href="{{ paginate.next.url }}">Next</a>
  {% endif %}
{% endpaginate %}

Default limit: 20 rows. Hard cap: 100.

Template Page Example

<article>
  <h1>{{ row.title }}</h1>
  <p>Published: {{ row.created_at | date: "%B %d, %Y" }}</p>
  <div>{{ row.body | markdownify }}</div>
  <a href="{{ row.url }}">Permalink</a>
</article>

Syntax Warnings

Liquid syntax errors are reported via liquid_warnings in page responses. Validation warnings (including filter chains inside {% for %} tags) appear in validation.warnings when creating or updating pages.