Tables turn PageWeave into a structured CMS. Content lives in rows; pages render it. Never hand-maintain text in two places.
Schema design
One table per content type. Common base fields:
slug— URL segment (you supply it; nothing auto-generates)title,subtitle,description— display + metaorder— number, position within sectiontags— space-separated string for filteringcontent— text, the canonical markdown body
Add fields only when a template needs them (code, preview_html, palette, section). Every field you add is a field every row must carry.
Template pages bind it together
A page at /library/:category/:slug maps each placeholder to a row field. With placeholder_defaults: {"lang": "en"}, rows with lang=en serve at the collapsed URL. The template renders:
<h1>{{ row.title }}</h1>
<div class="prose">{{ row.content | markdownify }}</div>
And the markdown body serves {{ row.content }} raw — the .md twin for free.
Bulk import workflow
- Build CSV locally (Python's csv module handles escaping).
- Upload as an asset (
request_upload_url→ PUT → asset URL). import_table_rows(table_id, source_url)— ≤25k rows syncs inline.- Round-trip with
export_table_csv; upsert mode matches on theidcolumn.
The reserved published_at column controls state: empty keeps current, -/draft unpublishes, now publishes, ISO 8601 schedules.
Draft workflow
Draft rows render ONLY on dev environments' template pages — perfect for staging content. Review on *.env.pageweave.site, then flip published_at: now.
Limits & scale
5k rows free / 100k Pro. Liquid loops stay cheap to ~2k rows; beyond that, use the public JSON API (GET /t/{table_id}) with client-side fetch. Template pages themselves scale fine at any size.