Facet Reference

Facet Reference

Use this page when the primary syntax guide is no longer enough and you need deeper reference material for Facet behavior, context objects, helper contracts, and rendering rules.

This page is intentionally dense. It is the technical reference companion to:

What This Page Covers

Use this reference when you need details about:

  • context objects and their common properties
  • page, media, and collection access patterns
  • image and map helper contracts
  • filters by category
  • Variable system behavior
  • layout-level slots
  • managed data attributes
  • media references
  • rendering and security rules

Context Objects

Facet templates have access to a controlled set of root-level objects. Which properties are useful depends on the current render surface, but the same mental model applies everywhere.

page

page is the current page object in the current render context.

Core Page Properties

Common scalar properties include:

  • page.id
  • page.name
  • page.title
  • page.url
  • page.httpUrl
  • page.path
  • page.template
  • page.created
  • page.modified
  • page.createdStr
  • page.modifiedStr
  • page.status
  • page.sort
  • page.numChildren

Typical usage:

<h1>{{ page.title }}</h1>
<p>Updated {{ page.modified | date("F j, Y") }}</p>

Status-style Properties

Common boolean-style properties include:

  • page.isPublished
  • page.isHidden
  • page.isUnpublished
  • page.isTrash
  • page.isNew
  • page.hasChildren
  • page.isEditable

Example:

{{#if page.hasChildren}}
  <p>This page has child pages.</p>
{{/if}}

page.isEditable is especially useful for UI that should only appear to editors of the current page:

{{#if page.isEditable}}
  <a href="{{ page.url }}?edit=1">Edit this page</a>
{{/if}}

Relational Properties

Facet commonly exposes relational access like:

  • page.parent
  • page.parents
  • page.rootParent
  • page.children
  • page.siblings
  • page.next
  • page.prev
  • page.find

Examples:

{{ page.parent.title }}
{{ page.rootParent.title }}

{{#each page.children as="child"}}
  <a href="{{ child.url }}">{{ child.title }}</a>
{{/each}}

Media Properties

Common media-oriented properties include:

  • page.images
  • page.image
  • page.files

Examples:

{{#each page.images as="img"}}
  <img src="{{ img.width(800).webp.url }}" alt="{{ img.description }}">
{{/each}}

{{#each page.files as="file"}}
  <a href="{{ file.url }}">{{ file.filename }}</a>
{{/each}}

Image Object Properties

Typical image object properties include:

  • img.url
  • img.httpUrl
  • img.filename
  • img.description
  • img.width
  • img.height
  • img.ext
  • img.filesize
  • img.webp.url
  • img.raw.url

File Object Properties

Typical file object properties include:

  • file.url
  • file.httpUrl
  • file.filename
  • file.description
  • file.ext
  • file.filesize

Image Transforms

Page and query image objects support chained transforms:

{{ page.image.width(800) }}
{{ page.image.size(600, 400) }}
{{ page.image.size(600, 400).webp.url }}
{{ page.image.raw.url }}

In loops:

{{#each page.images as="img"}}
  <img src="{{ img.size(400, 300).webp.url }}" alt="{{ img.description }}">
{{/each}}

Collection Methods

Common collection helpers include:

  • first
  • last
  • count
  • eq(n)
  • slice(start, length)

Examples:

{{ page.images.first.url }}
{{ page.images.count }}
{{ page.images.eq(2).url }}

Dynamic Field Access

Custom page fields can be accessed by name:

{{ page.summary }}
{{ page.body }}
{{ page.headline }}
{{ page.field name="my_custom_field" }}

Use this when the current render surface really depends on page-level data. Do not use it to hide a weak Component field contract.

Localized URL

Facet may expose localized URL helpers for page-aware multilingual rendering:

{{ page.localUrl lang="fr" }}
{{ page.localUrl lang="default" }}

user

user represents the current user context.

Common properties include:

  • user.id
  • user.name
  • user.title
  • user.email
  • user.displayName
  • user.isLoggedin
  • user.isGuest
  • user.isSuperuser
  • user.language

Typical usage:

{{#if user.isLoggedin}}
  <span>Hello, {{ user.displayName }}!</span>
{{else}}
  <a href="/login/">Log In</a>
{{/if}}

pages

pages exposes query-oriented helpers for scalar or aggregate outputs.

Common patterns include:

  • pages.count selector="..."
  • pages.first selector="..."
  • pages.last selector="..."
  • pages.titles selector="..." sep=", "
  • pages.json selector="..."

Examples:

<p>Total posts: {{ pages.count selector="template=blog-post" }}</p>
<p>Categories: {{ pages.titles selector="template=category" sep=", " }}</p>

Use {{#pages}} when you need a block loop. Use inline pages.* helpers for scalar outputs.

languages / lang

languages and lang expose the language system.

Common properties include:

  • languages.count
  • languages.current
  • languages.default
  • languages.isMultiLanguage
  • languages.all
  • languages.{name}

Example:

{{#if languages.isMultiLanguage}}
  Current language: {{ languages.current.title }}
{{/if}}

Inside {{#languages}}, each language item commonly exposes:

  • lang.id
  • lang.name
  • lang.title
  • lang.isDefault
  • lang.isCurrent
  • lang.url
  • lang.httpUrl
  • lang.locale

site

site provides site-level values.

Common properties include:

  • site.name
  • site.url
  • site.httpUrl
  • site.httpHost
  • site.locale
  • site.year
  • site.assets
  • site.templates
  • site.adminUrl
  • site.adminProfileUrl
  • site.adminLogoutUrl

Settings-style access may also be available:

{{ site.setting key="company_name" }}
{{ site.setting key="phone" }}

Admin-oriented site helpers are useful for authenticated utility UI:

<a href="{{ site.adminUrl }}">Dashboard</a>
<a href="{{ site.adminProfileUrl }}">Profile</a>
<a href="{{ site.adminLogoutUrl }}">Log out</a>

Use site-level settings for stable global values, not for content that should belong to a Variable or a managed page-level object.

now

now provides current time values.

Common properties include:

  • now.timestamp
  • now.date
  • now.datetime
  • now.year
  • now.month
  • now.day

Example:

<footer>&copy; {{ now.year }} {{ site.name }}</footer>

attr

attr exposes values passed into a Variable:

{{! Usage: [[my-widget title="Hello" color="blue"]] }}
<div style="color: {{ attr.color | default("black") }}">
  <h3>{{ attr.title | default("Default Title") }}</h3>
</div>

attr should stay small and explicit. If a fragment needs a large editable data model, it probably wants a Component instead of a Variable.

loop

loop is available inside loop blocks such as:

  • {{#each}}
  • {{#pages}}
  • {{#languages}}

Common properties:

  • loop.index
  • loop.index1
  • loop.first
  • loop.last
  • loop.length
  • loop.even
  • loop.odd

Example:

{{#each page.children as="child"}}
  <div class="{{#if loop.odd}}row-alt{{/if}}">
    {{ loop.index1 }}. {{ child.title }}
  </div>
{{/each}}

this

Inside {{#each}}, this is the current loop item:

{{#each page.children}}
  <a href="{{ this.url }}">{{ this.title }}</a>
{{/each}}

this and a named alias normally point to the same current item.

@index

@index is shorthand for loop.index.

Example:

{{#each page.children as="child"}}
  {{#if @index == 0}}
    <h2>{{ child.title }}</h2>
  {{else}}
    <p>{{ child.title }}</p>
  {{/if}}
{{/each}}

Component Root-Level Fields

In Component templates, the current Component instance fields are often injected as root-level variables:

<h1>{{ title }}</h1>

{{#if showGallery}}
  <div class="gallery">
    {{#each images as="img"}}
      <img src="{{ img.url }}">
    {{/each}}
  </div>
{{/if}}

This is one reason Component templates feel simpler than generic page-level templates.

Reserved names should not be reused casually as custom field names if they would collide with core context objects such as:

  • page
  • pages
  • user
  • languages
  • lang
  • site
  • now
  • attr
  • loop
  • this
  • @index

Component Field Helper Reference

Facet works with several Component-specific helper contracts that are not the same as general {{ expr }} output.

Single Image Field Helpers

Single image fields commonly support:

  • {field}
  • {field.raw}
  • {field.webp}
  • {field.name}
  • {field.description}
  • {field.width(N)}
  • {field.width(N).webp}
  • {field.height(N)}
  • {field.height(N).webp}
  • {field.size(W,H)}
  • {field.size(W,H).webp}

Example:

<img src="{hero.width(1200).webp}" alt="{hero.description}">
<a href="{photo.raw}" download>Download original</a>

Multi-image Field Helpers

Inside {{#each gallery}}, images items commonly support:

  • {{this}}
  • {{this.raw}}
  • {{this.webp}}
  • {{this.name}}
  • {{this.description}}
  • {{this.width(N)}}
  • {{this.width(N).webp}}
  • {{this.height(N)}}
  • {{this.height(N).webp}}
  • {{this.size(W,H)}}
  • {{this.size(W,H).webp}}

Example:

{{#each gallery}}
  <figure>
    <img src="{{this.width(600).webp}}" alt="{{this.description}}">
    <figcaption>{{this.name}}</figcaption>
  </figure>
{{/each}}

Map Field Helpers

map fields commonly support:

  • {field}
  • {field.width(N)}
  • {field.height(N)}
  • {field.size(W,H)}

Examples:

{officeLocation}
{officeLocation.width(600)}
{officeLocation.size(800,500)}

Use map only for structured location data, not for plain address copy.

Managed Embed Contracts

Managed workflow embeds typically use:

  • data-form-embed
  • data-review-embed

Examples:

<div data-form-embed="enterprise-demo-request"></div>
<div data-review-embed="customer-success-reviews"></div>

<div data-form-embed="{contactForm}"></div>
<div data-review-embed="{serviceReview}"></div>

Use fixed ids for template-owned workflows and field-backed values for author-selectable workflows.

Filters Reference

Filters transform values before output. The most important rule is simple:

  • use filters for formatting
  • do not use filters to hide a weak object model

String Filters

Common string filters include:

  • escape / e
  • raw
  • upper
  • lower
  • capitalize
  • trim
  • truncate
  • nl2br
  • striptags
  • replace
  • split
  • slice
  • pad
  • wrap
  • slug

Examples:

{{ page.title | upper }}
{{ page.body | striptags | truncate(200, "...") }}
{{ text | nl2br }}
{{ title | slug }}

Default Value Filter

default supplies a fallback when the input is missing or null-like in the supported contract:

{{ page.subtitle | default("Untitled") }}

If the exact empty-string behavior matters, prefer an explicit ternary.

Number Filters

Common number filters include:

  • number
  • currency
  • abs
  • round
  • ceil
  • floor

Examples:

{{ 1234567 | number(0, ".", ",") }}
{{ price | currency("$", 2) }}
{{ ratio | round(2) }}

Date Filters

Common date filters include:

  • date
  • datetime
  • relative

Examples:

{{ page.created | date("F j, Y") }}
{{ page.modified | datetime("Y-m-d H:i") }}
{{ page.modified | relative }}

URL Filters

Common URL-oriented filters include:

  • url_encode
  • url

Examples:

{{ page.title | url_encode }}
{{ website | url }}

JSON Filter

Use json for safe JSON output:

{{ data | json }}

This is useful when the template needs structured data in HTML or script-oriented output.

Array and Collection Filters

Common collection filters include:

  • join
  • first
  • last
  • count
  • length
  • reverse
  • sort
  • pluck
  • unique
  • batch
  • keys
  • values
  • merge

Examples:

{{ tags | pluck("title") | join(", ") }}
{{ page.children | count }}
{{ items | batch(4) }}

Image Filter

Common image filter:

  • size

Example:

{{ page.image | size(600, 400) }}

Hash Filter

Common hash filter:

  • md5

Example:

{{ user.email | md5 }}

Variable System

Variables are reusable template fragments that can be embedded anywhere supported by FaceFlow rendering.

Including Variables

Basic include:

[[my-variable]]

With attributes:

[[my-widget title="Hello World" color="blue" count="5"]]

Variable Structure

A Variable typically contains:

  • HTML template
  • optional CSS
  • optional JS
  • optional attributes
  • cache mode

Cache Modes

Common modes include:

  • static
  • dynamic
  • auto

Use:

  • static when the output is broadly cache-friendly
  • dynamic when the output depends on user- or request-sensitive runtime state
  • auto when the engine should choose based on the Variable behavior

Nested Variables

Variables can include other Variables:

<footer>
  [[copyright-notice year="2026"]]
  [[social-links]]
</footer>

Use nesting to improve reuse and clarity, not to create deep hidden dependency chains.

Layout-Level Syntax

Layout-oriented templates often rely on a few structural placeholders:

  • {content}
  • {header}
  • {footer}
  • {siteComponents}

Example:

<body>
  {header}
  {siteComponents}
  <main>
    {content}
  </main>
  {footer}
</body>

These placeholders belong to layout and shell composition, not ordinary section rendering.

Data Attributes

Common runtime-oriented data attributes include:

  • data-form-embed
  • data-review-embed
  • data-fb-map
  • data-fb-map-field
  • data-component-*
  • data-form-ajax
  • data-variable
  • data-loading

Use these when the runtime contract explicitly documents them. Do not invent parallel attributes casually.

Media References

Some FaceFlow rendering contexts may expose managed media reference patterns such as:

  • @image:filename
  • @file:filename

These are implementation-side representations of managed assets. In authored templates, prefer the documented helper or object output forms rather than relying on raw storage references.

Rendering Pipeline

A practical Facet rendering order usually looks like this:

  1. load the owning FaceFlow object
  2. resolve the current render context
  3. inject root-level fields and context objects
  4. evaluate field helper contracts
  5. evaluate Facet expressions and block helpers
  6. resolve Variable embeds
  7. resolve managed runtime markers such as Forms, Reviews, or maps
  8. return final output

The important design lesson is that Component field shorthand, Facet expressions, Variable embeds, and managed runtime markers are related but not identical stages.

Security Model

Facet is designed as a governed template layer rather than a raw code execution environment.

Key rules:

  • no arbitrary server-side code execution
  • root objects are controlled
  • filters and block types are whitelisted
  • query behavior is constrained
  • raw output should be used intentionally

Treat this as a sandboxed template language, not as a fallback scripting engine.

Practical Design Rules

  • prefer clear object boundaries over clever template logic
  • keep template logic shallow
  • use Variables for fragments and Components for section contracts
  • use Lists when query and pagination are part of the contract
  • review any field helper syntax against the field reference before using it
  • review cache-sensitive content before introducing deferred rendering

Cookbook Patterns

<nav>
  <ul>
    {{#pages selector="parent=1, sort=sort" as="item"}}
      <li><a href="{{ item.url }}">{{ item.title }}</a></li>
    {{/pages}}
  </ul>
</nav>

Blog Post List

{{#pages selector="template=blog-post, sort=-created, limit=6" as="post"}}
  <article>
    <h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
    <p>{{ post.body | striptags | truncate(250) }}</p>
  </article>
{{else}}
  <p>No blog posts yet.</p>
{{/pages}}

Multi-language Switcher

{{#if languages.isMultiLanguage}}
  {{#languages as="lang"}}
    <a href="{{ lang.url }}">{{ lang.title }}</a>
  {{/languages}}
{{/if}}
{{#each page.parents as="p"}}
  <a href="{{ p.url }}">{{ p.title }}</a> /
{{/each}}
<span>{{ page.title }}</span>

Cache-safe User Welcome

{{#deferred skeleton-width="10rem" skeleton-height="2rem"}}
  {{#if user.isLoggedin}}
    <span>{{ user.displayName }}</span>
  {{else}}
    <a href="/login/">Sign In</a>
  {{/if}}
{{/deferred}}

FAQ

When should I use {{#pages}} vs {{#each}}?

  • use {{#each}} when the data already exists in the current context
  • use {{#pages}} when the template must query the data

What is the difference between {{ }} and {{{ }}}?

  • {{ ... }} is escaped output
  • {{{ ... }}} is raw output

Use escaped output by default.

Can I nest loops?

Yes, but only when the object model remains readable. If nested loops are compensating for a weak content contract, stop and revisit the design.

Why does a field return null or empty output?

Common causes:

  • wrong context
  • wrong field name
  • using the wrong syntax family for the current object
  • expecting a helper on a field that does not support it

How should I debug template problems?

Start in this order:

  1. confirm the owning object contract
  2. confirm the right syntax family
  3. confirm the available context objects
  4. reduce the template to the smallest reproducible fragment
  5. then rebuild from there