Skip to main content
This page describes the directives and expression syntax of the frontend template engine.
Use [[ ]] for frontend expressions. {{ }} is reserved for the backend Template Engine. Both can appear on the same page.

Conditionals: template-if, template-else-if, template-else

Show one block among several based on a JavaScript expression evaluated in the current scope.

template-if

  • Attribute: data-condition="expression"
  • Behavior: When the expression is truthy, the element’s inner HTML is rendered; otherwise it is not. The <template-if> element itself is replaced by that content.

Sibling template-else

A <template-else> immediately after a <template-if> (or after a chain of template-else-if) is the fallback when no condition matches. It has no data-condition.

template-else-if

Chain multiple conditions. The first matching branch is shown; if none match, the optional template-else is shown.
template-else always needs a preceding template-if. There is no standalone “else only” block. Use template-else-if for the middle branches, not a tag like template-if-else.

What you can use in data-condition

  • Dot paths: checkout.coupon.applied, query.coupon, items.length
  • Comparisons: cartItems.length > 0, item.quantity === 1
  • Functions: cartCount(cartItems) > 0, checkout.selectedBumpLines && checkout.selectedBumpLines.length > 0
  • Literals: data-condition="true" or data-condition="false" are supported
In HTML attributes, escape characters that would break the attribute: use &gt; for >, &amp; for &, &lt; for < (e.g. data-condition="cartItems.length &gt; 1"). Expressions run in a context that includes window.efScope and any loop variable (e.g. item inside a template-foreach).

Loops: template-foreach

Repeat a block once per element in an array (or iterable). The element is replaced by the repeated content.

Preferred form: data-each

  • item — Variable name for the current element (you can choose any name).
  • cartItems — Expression that must evaluate to an array (or iterable) in scope.

Optional index

Use (item, i) in array or item, i in array to get a zero-based index:

Inline data-each on regular elements

Instead of wrapping content in a <template-foreach> tag, you can place data-each directly on the repeated element itself. This is required for <option> elements inside a <select> — Safari and iOS do not allow custom elements like <template-foreach> inside <select>, so the options will not render.
Use data-ef-value to bind the option’s value attribute and data-ef-cloak to hide the template row until the engine expands it.
Do not use <template-foreach> inside <select>. Safari and iOS strip unknown elements from <select>, so <option> elements wrapped in <template-foreach> will not appear. Always use inline data-each on the <option> tag directly.

Aliases and fallbacks

  • data-for — Alias for data-each (same meaning).
  • Legacy: You can use data-item, data-index, and data-array instead of data-each if needed. data-item and data-array can also be used together with data-each when the engine expects them (e.g. for internal loop variable names), as in some production checkout templates.

Empty arrays

When the array is empty, the loop renders nothing. Some integrations support an optional empty content (e.g. “Your cart is empty”) via configuration; the default behavior is to output no nodes.

Scope inside the loop

Inside the block, item (and i if you declared it) are in scope. You can also access parent scope (e.g. checkout.currency, cartItems). Nested <template-if> and <template-foreach> are supported.

Variables: template-set and template-vars

template-set

Define a local variable for the template. The element is removed after processing; only the variable is set in the current context.
  • data-variable="name" — Variable name (single segment, e.g. total or label).
  • data-value="expression" — Expression to evaluate; result is stored in name.
Use when you need to reuse a computed value or keep markup readable.

template-vars / <template data-ef-vars>

Enables [[ expression ]] in the content of the element without requiring a wrapping template-if or template-foreach. The element is replaced by its inner content; that content is then processed (conditions, loops, and variable replacement run on it). Use <template-vars> or <template data-ef-vars> when you have a block where the only dynamic part is [[ ... ]] and you want to avoid an extra conditional.
var.* is backend-only, by design. Brand variables belong to the server-side template engine — print them with {{ var.offer_name }}. They are deliberately never copied into window.efScope (the browser gets the values a page chose to print, not the brand’s variable set), so [[ var.something ]] has nothing to resolve against.To use one client-side, hand it over explicitly: the server prints the value, the page stores it under a name you choose.
See Rendering pipeline.

template-component — does not exist

There is no frontend component include. <template-component data-component-name="…"> appears in some older reference tables, but no engine resolves it — not the frontend processor, not the backend template engine, not the page pipeline. It reaches the browser as an inert unknown element and, being normally empty, renders nothing at all with no error.Include a component with the backend directive @component("code", { key: value }) instead. See Rendering pipeline.

Expressions: [[ ... ]]

[[ expression ]] is replaced by the result of the expression, evaluated in the current scope (global scope + loop/block context).

Where it works

  • Text nodes: Price: [[ item.price ]]
  • Attribute values: src="[[ item.image ]]", data-code="[[ item.code ]]", required="[[ f.required ? 'required' : '' ]]"
  • Anywhere inside processed template content (e.g. inside template-if or template-foreach)
For cart/checkout control attributes, use data-code="[[ item.code ]]" so the plugin receives the actual product code string. Unquoted data-code="item.code" may still be supported by the engine in some contexts.

What you can write inside [[ ]]

  • Dot paths: item.name, checkout.total, checkout.customer.email
  • Expressions: item.price * item.quantity, item.open ? 'Hide' : 'Show'
  • Function calls: formatPrice(item.price, currency), upper(item.name), cartCount(cartItems)
Functions available in expressions include built-in helpers and any registered via registerTemplateFunction.

Examples

Checkout and cart display values (e.g. checkout.total, item.total, item.price, bump.price) are usually already formatted as currency strings. Use them as-is (e.g. [[ checkout.total ]]). Do not pass them to formatPrice() again. Use formatPrice only for raw values (e.g. checkout.total_raw, bump.price_raw) when you need to display a number. See Built-in functions and Limitations and pitfalls.

Attribute binding shortcuts (ef-*)

For many UI components, attribute bindings are easier to maintain than inline token strings:
Supported aliases include ef-text, ef-html, ef-src, ef-href, ef-title, ef-alt, ef-placeholder, and ef-value (plus data- forms). These can be added from the page builder Data Attributes panel (right sidebar) when an element is selected.

Cloaking

To avoid a flash of raw [[ ... ]] or the wrong branch before the template engine runs:
  • [data-ef-cloak] — Add this attribute to elements that should be hidden until processed. The engine may remove it after processing. Include CSS: [data-ef-cloak] { display: none !important; }.
  • Template tags — Include CSS that hides the custom elements until processed: template-foreach, template-if, template-else, template-set with display: none !important.
  • In some deployments, data-frontend-template="true" is used on template-if, template-else, and template-foreach to mark frontend-processed blocks.

Processing order

The engine runs directives in a fixed order:
  1. templateVars — Unwrap <template-vars> / <template data-ef-vars> so inner [[ ]] can be processed.
  2. set — Process <template-set> (set variables, remove elements).
  3. foreach — Expand <template-foreach> (in a one-shot process path) or re-render mounted foreach on update.
  4. if — Expand <template-if> (and sibling else-if/else) or re-render mounted if on update.
  5. vars — Replace remaining [[ ... ]] in text and attributes.
On initial load, template-if and template-foreach are mounted: the original tags are replaced by comment anchors and an update function is registered. When reactive scope changes happen, those functions re-evaluate and only affected parts of the DOM update. Next: Scope & Data.