loftur

Lifecycles

Content lifecycle: draft / published

Content has its own draft/published state at the record level, independent of both code and schema. Editors publish content without a deploy and without an engineer.

Record state

Records on a draft-enabled model start as draft. Required-field and uniqueness validation is deferred until you publish. set_publish_status moves a record between draft and published (with bulk variants); publishing snapshots the record, unpublishing clears the snapshot.

  • Draft — editable, visible only in preview.
  • Published — frozen into a published snapshot the live site reads from.

How draft vs published is decided

A single boolean, includeDrafts, is threaded from the render path down to the query. Crucially, it's bound to the runtime, not to the request — a visitor can't opt into drafts:

Render pathincludeDraftsReads
servePublished (live site)falsepublished snapshots only
serveDraft (preview cookie)truelive draft columns

So the published site can never leak an unpublished record, and a site-code preview shows drafts automatically — the same URL, the same runtime, just the draft flag flipped.

Content changes need no publish_site. The live site re-reads content every request through its GraphQL capability; a newly published record is live on the next request. Code deploys and content publishing are entirely separate events.

Previewing unpublished content

To review draft content against a running site, use a preview_site URL — the draft cookie selects the draft render path, which reads drafts. That shows all draft records in context.

The CMS also mints its own per-record preview tokens (get_preview_url). Those are not currently wired into the Loftur render path — draft content preview goes through the site's preview_site cookie, not the per-record token. Reach for preview_site when you want to see drafts.

Assets and localization

The same content tools cover assets (upload, import from URL, replace) and localized fields via built-in locales — all editable content, all on the content loop, none of it requiring a code publish.