DOM Bindings
FQL renders state and data into the DOM through f-* attributes. Elements that should be controlled by Frontbacked need f="true".
<h1 f="true" f-text="$state.title">Loading...</h1>
When $state.title changes, the element text updates.
Text Binding
Use f-text to set textContent.
<h1 f="true" f-text="$state.product.title || 'Untitled product'">Untitled product</h1>
<p f="true" f-text="$state.product.summary || ''"></p>
If the expression resolves to null or undefined, the text becomes empty.
Attribute Binding
Use f-attr-* to set an attribute.
<a
f="true"
f-attr-href="$state.product.checkoutUrl"
f-attr-title="$state.product.title"
>
Buy now
</a>
The part after f-attr- becomes the real attribute name.
Examples:
| FQL attribute | Real attribute |
|---|---|
f-attr-href | href |
f-attr-src | src |
f-attr-alt | alt |
f-attr-aria-checked | aria-checked |
f-attr-data-state | data-state |
If the expression resolves to null or undefined, Frontbacked removes the attribute.
Multiple State Sources
When a page already has more than one element that can update the same state value, declare those sources with a comma. This is useful when generated markup has a visible control and a separate label or helper element that should behave like the same input.
<head>
<!-- {STATE}
state = {
"terms(false)": "#terms.onchange.target.checked, #termsButton.onclick.target.toggleTerms()",
"termsLabel": "$state.terms.termsLabel()"
}
-->
</head>
<body>
<input id="terms" type="checkbox">
<button
id="termsButton"
type="button"
f="true"
f-attr-aria-checked="$state.terms"
f-attr-data-state="$state.termsLabel"
>
Accept terms
</button>
</body>
function toggleTerms() {
return !Frontbacked.getState("terms");
}
function termsLabel(value) {
return value ? "checked" : "unchecked";
}
window.toggleTerms = toggleTerms;
window.termsLabel = termsLabel;
The checkbox and button both update $state.terms. This is not a required UI pattern. It is just one example of using multiple state sources when a theme's markup already gives you more than one way to represent the same value.
Lists
Use f-list to repeat the first child of an element for each item in a fetched list.
<!-- {STATE}
state = {
"publishedProducts": {
"$list": "products",
"$where": { "status": "published" },
"$order": { "createdAt": "desc" },
"$limit": 6
}
}
-->
<ul
id="productList"
f="true"
f-list="$state.publishedProducts"
>
<li>
<a f="true" f-attr-href="$.slug">
<span f="true" f-text="$.title || 'Untitled product'">Untitled product</span>
</a>
</li>
</ul>
List rules:
- The list root must have an
id. - The first child is used as the template.
- Inside the template,
$points to the current list item. - Nested
f="true"elements inside the template can usef-textandf-attr-*.
For settings-backed lists, define a state selector whose source points to the settings path:
<!-- {STATE}
state = {
"services": { "$list": "$settings.home.services.items" }
}
-->
<section id="serviceList" f="true" f-list="$state.services">
<article>
<span class="icon">...</span>
<h3 f="true" f-text="$.title">Default service</h3>
<p f="true" f-text="$.body">Default service copy.</p>
</article>
<article>
<span class="icon">...</span>
<h3>Second default service</h3>
<p>Second default service copy.</p>
</article>
</section>
The first child is the saved-data template. All authored children are shown when no saved settings array exists yet, which makes them useful as defaults or showcase content for a fresh site. Once the backend has a saved array, even an empty array, the saved array controls the public render. If the saved array has one item, only one live item is rendered. If the admin clears the list, no live items are rendered.
In edit mode, missing authored defaults are shown as grey restore suggestions when the saved array is shorter than the authored defaults. Each suggestion has a button that adds that default back to the local draft. An empty saved list also shows an Edit List button so the admin can add custom items or restore all theme defaults.
In edit mode, a list whose selector source starts with $settings. is not edited as a raw array. Frontbacked opens a list editor from the fields in the first template, such as $.title and $.body, so decorative icons or fixed theme markup can stay in the theme.
List Selectors
Use one selector object for source, filtering, sorting, and limits:
{
"categoryProducts": {
"$list": "products",
"$where": { "category": "$query.category", "price.$gte": 1000 },
"$order": { "createdAt": "desc" },
"$limit": 12
}
}
Plain keys inside $where are equality checks. Comparison suffixes include $gt, $gte, $lt, $lte, and $ne. $and and $or accept arrays for grouped logic.
Pagination
Use f-insert-pagination or f-replace-pagination to let Frontbacked generate responsive pagination controls for a list.
<ul id="categoryProducts" f="true" f-list="$state.categoryProducts">
<li>
<span f="true" f-text="$.title"></span>
</li>
</ul>
<div f-insert-pagination="#categoryProducts"></div>
You can also target the selector expression: <div f-insert-pagination="$state.categoryProducts"></div>. Use f-replace-pagination when the host element itself should become the generated pagination UI.
Binding Tips
Keep fallback text in the HTML so the page has a sensible first paint before Frontbacked initializes.
Use f-attr-* for links, images, ARIA state, and data attributes. Use f-text for visible text.
Put f="true" only on elements that need Frontbacked rendering. For lists, include it on the list root and on controlled elements inside the template.