Writing f-templates
An <f-template> element associates a declarative HTML template with a custom element by name. The inner <template> contains the markup that becomes the element's shadow DOM.
<f-template name="my-element">
<template>
<!-- template content here -->
</template>
</f-template>
The name attribute must match the custom element tag name registered in JavaScript. A single HTML file can contain multiple <f-template> elements for different components.
Binding Syntax
Declarative templates use a handlebars-like syntax for data binding. There are two categories of bindings based on when they are evaluated:
| Syntax | Evaluated | Purpose |
|---|---|---|
{{expression}} |
Server and client | Content and attribute bindings (HTML-escaped) |
{{{expression}}} |
Server and client | Unescaped HTML content binding |
{expression} |
Client only | Event handlers and attribute directives |
Server-evaluated bindings ({{}} and {{{}}}) are resolved during server-side rendering and hydrated on the client. Client-only bindings ({}) are ignored by the server and activated when the component connects in the browser.
Content Bindings
Use double curly braces to bind text content:
<f-template name="greeting-card">
<template>
<h3>{{greeting}}</h3>
<p>Welcome, {{name}}!</p>
</template>
</f-template>
Content bindings are HTML-escaped for security. To render raw HTML, use triple curly braces:
<f-template name="rich-content">
<template>
{{{htmlContent}}}
</template>
</f-template>
Triple-brace bindings render unescaped HTML through an innerHTML binding with an additional wrapping div. The default DOMPolicy blocks that innerHTML write unless the host app configures an innerHTML guard. Only use triple-brace bindings with trusted or sanitized content to avoid cross-site scripting (XSS) vulnerabilities, and follow the DOMPolicy and Trusted Types guide.
Attribute Bindings
Bind element attributes using double curly braces inside attribute values:
<f-template name="styled-input">
<template>
<input type="{{inputType}}" class="{{cssClass}}">
</template>
</f-template>
Boolean Attributes
Prefix the attribute name with ? for boolean attribute bindings. When the value is truthy, the attribute is present; when falsy, it is removed:
<f-template name="toggle-button">
<template>
<button ?disabled="{{isDisabled}}">Click me</button>
</template>
</f-template>
Boolean bindings also support expressions with comparison operators:
<button ?disabled="{{status == 'locked'}}">Submit</button>
Event Bindings
Event bindings use the @ prefix and single curly braces (client-only). The handler name must include parentheses ():
<f-template name="click-counter">
<template>
<button @click="{handleClick()}">Count: {{count}}</button>
</template>
</f-template>
Event Arguments
You can pass special arguments to event handlers:
| Argument | Description |
|---|---|
$e |
The DOM event object |
$c |
The execution context |
$c.parent |
The parent execution context (useful inside <f-repeat>) |
$c.event |
The DOM event from the execution context |
| Any other token | Resolved as a binding path on the data source |
Examples:
<!-- Pass the DOM event -->
<button @click="{handleClick($e)}">Click</button>
<!-- Pass the execution context -->
<button @click="{handleClick($c)}">Click</button>
<!-- Pass multiple arguments -->
<button @click="{handleClick($e, $c)}">Click</button>
<!-- Pass a data binding as an argument -->
<button @click="{handleClick(user.id)}">Click</button>
Event bindings use single curly braces {...} because they are client-only — the server does not need to evaluate them.
Dot-Notation Paths
Binding expressions support dot-notation for accessing nested properties:
<f-template name="user-card">
<template>
<p>{{user.name}}</p>
<p>{{user.address.city}}, {{user.address.state}}</p>
</template>
</f-template>
Directives
Directives extend template behavior with conditional rendering, list iteration, element references, and more. Directives that serve as containers use the <f-*> element syntax. Directives that attach to an existing element use the f-* attribute syntax.
Conditional Rendering (f-when)
The <f-when> directive renders its content only when the condition is truthy:
<f-template name="user-status">
<template>
<f-when value="{{isLoggedIn}}">
<p>Welcome back, {{username}}!</p>
</f-when>
<f-when value="{{!isLoggedIn}}">
<p>Please log in.</p>
</f-when>
</template>
</f-template>
Conditions support these comparison operators:
| Operator | Description |
|---|---|
== |
Equal |
!= |
Not equal |
> |
Greater than |
>= |
Greater than or equal |
< |
Less than |
<= |
Less than or equal |
|| |
Logical OR |
&& |
Logical AND |
The right operand can be a string ('bar'), boolean (true/false), number (3), or another binding path:
<f-when value="{{status == 'active'}}">Active</f-when>
<f-when value="{{count > 0}}">Has items</f-when>
<f-when value="{{role == adminRole}}">Admin panel</f-when>
List Iteration (f-repeat)
The <f-repeat> directive iterates over an array and renders its content for each item:
<f-template name="todo-list">
<template>
<ul>
<f-repeat value="{{item in items}}">
<li>{{item.text}}</li>
</f-repeat>
</ul>
</template>
</f-template>
Inside <f-repeat>, bindings without a context prefix resolve to the host element, not the repeat item. Use the declared variable name (e.g. item) to access the current iteration value:
<f-repeat value="{{item in list}}">
<li>{{item}} - {{title}}</li>
<!-- {{item}} is the current list item -->
<!-- {{title}} resolves to the host element's title property -->
</f-repeat>
Accessing the Parent Context
Inside <f-repeat>, use $c.parent to access the host element's methods and properties from event handlers:
<f-repeat value="{{item in items}}">
<button @click="{$c.parent.handleItemClick($e)}">
{{item.name}}
</button>
</f-repeat>
Nested Directives
<f-when> can be used inside <f-repeat> and vice versa:
<f-repeat value="{{item in items}}">
<f-when value="{{item.visible}}">
<span>{{item.name}}</span>
</f-when>
</f-repeat>
Element Reference (f-ref)
The f-ref attribute directive stores a reference to a DOM element on the component instance:
<f-template name="video-player">
<template>
<video f-ref="{videoElement}"></video>
<button @click="{play()}">Play</button>
</template>
</f-template>
Slotted Content (f-slotted)
The f-slotted attribute directive observes elements assigned to a <slot>:
<f-template name="card-container">
<template>
<slot f-slotted="{slottedItems}"></slot>
<slot f-slotted="{slottedItems filter elements()}"></slot>
<slot f-slotted="{slottedItems filter elements(div, p)}"></slot>
</template>
</f-template>
Children (f-children)
The f-children attribute directive observes child elements:
<f-template name="item-list">
<template>
<ul f-children="{listItems}">
<f-repeat value="{{item in list}}">
<li>{{item}}</li>
</f-repeat>
</ul>
</template>
</f-template>
Attribute directives (f-ref, f-slotted, f-children) use single curly braces {...} because they are client-only.
Execution Context
In imperative FAST templates, every binding receives the data source and the execution context: ${(x, c) => ...}. Declarative templates access the same execution context using the $c prefix.
This is especially useful inside <f-repeat> blocks:
<!-- Call a method on the host element from inside a repeat -->
<f-repeat value="{{item in items}}">
<button @click="{$c.parent.handleItemClick($c.event)}">
{{item.name}}
</button>
</f-repeat>
<!-- Conditionally render using a host property inside a repeat -->
<f-repeat value="{{item in items}}">
<f-when value="{{$c.parent.showDetails}}">
<span>{{item.description}}</span>
</f-when>
</f-repeat>
Expression Limitations
Declarative template expressions are not arbitrary JavaScript. They support:
- Simple property access:
{{propertyName}} - Dot-notation paths:
{{object.property.nested}} - Negation:
{{!value}} - Comparison operators:
==,!=,>,>=,<,<= - Logical operators:
||,&& - Context access:
$c,$c.parent,$e - Iteration variables:
{{item in array}}
Complex JavaScript expressions, method calls in bindings (other than event handlers), and ternary operators are not supported.