Skip to content

Sections & data

Charities don’t edit pages, they compose them from sections. Each page holds an ordered list of sections drawn from a closed catalogue; each section carries typed content the charity authored in their admin. Your template’s job is to render whatever subset of that they actually wrote.

A section renderer receives exactly two things (see Render context):

  • section, the typed content of the section being rendered. Its shape is the section type’s fields; consult the catalogue per type.
  • brand, brand.name and brand.tagline, the charity’s identity.

That’s the whole surface. No API access, no other tenants, no secrets, and the context only ever grows within a contract major, so new variables can appear but existing ones never vanish or change shape.

Every {{ output }} is HTML-escaped. The single opt-out is | raw, and the only fields you should ever pass through it are the catalogue’s richtext fields (like hero.bodyHtml), the platform sanitises those server-side on save, before a template ever sees them.

<h1>{{ section.title }}</h1> <!-- escaped: always safe -->
{{ section.bodyHtml | raw }} <!-- sanitised richtext: the one legitimate raw -->
{{ section.title | raw }} <!-- NEVER: plain text doesn't get raw -->

Sparse data is normal, degrade, don’t decorate

Section titled “Sparse data is normal, degrade, don’t decorate”

Every section is independently omittable, and inside a section most fields are optional. The validator renders your template against each type’s minimal fixture, the least a charity can author, and your renderer must produce something sensible.

The pattern: wrap optional output in a condition, or give it an honest default.

{% if section.eyebrow %}<p class="lq-kicker">{{ section.eyebrow }}</p>{% endif %}
<h2>{{ section.heading | default: "What guides us" }}</h2>

Derive or omit, never fabricate. A template must not invent claims on a charity’s behalf: no placeholder impact numbers (“10,000 meals served”), no fake testimonials, no invented history. If the charity didn’t author it and the platform can’t derive it from real data, the space doesn’t render. A generic default like a heading (“What guides us”) is fine; a factual-sounding claim is not. This is a publish-gate policy, not just style guidance, template review checks for it.

Kind What it is How to render
text Plain text {{ section.field }} (escaped)
richtext Sanitised HTML from the platform editor `{{ section.field
url A link, scheme-guarded on save href="{{ section.field }}"
icon An emoji, or a Lucide icon name such as heart-handshake {{ item.icon }}. When the name is a Lucide icon the platform also supplies item.iconSvg (platform-generated markup, safe for raw): `{% if item.iconSvg %}{{ item.iconSvg
items A list of objects (see the type’s itemFields) {% for item in section.items %}

These sections have no author-editable fields in the admin, their content is the tenant’s live data, provided to your renderer:

Section Context What it is
events events[] Upcoming ticketed events (curated event shape, name, startsAt, venueName, priceFrom, soldOut, detailHref…)
whatsOn infoEvents[] Info events (special days, no sign-up), same shape
articles latestArticles[] Newest published articles (up to 6): title, href, excerpt, imageUrl, publishedAt, readingMinutes, categories
campaigns campaigns[], campaignsLabel Live campaigns in display order (title, href, summary, imageUrl) plus the tenant’s chosen section label, always link via href
appealGrid, emergency causes[] Active appeals with target, raised amount, progress percentage and the platform donation href
programmes services[] Published services with summary, image and platform-provided href
resources resources[] Public document links only; private media and storage metadata are excluded
locations locations[] Public venues with safe directions links; provider references and admin notes are excluded

Render them your way, or place the section’s default island and ignore the context entirely, see islands. Empty lists must render nothing: derive or omit.

Collection drops are deliberately curated projections rather than raw service responses. Use their supplied href or url fields. Do not construct storage, payment, event or service routes inside a template.