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.data.title || 'Untitled product'">Untitled product</h1>
<p f="true" f-text="$state.product.data.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.data.checkoutUrl"
f-attr-title="$state.product.data.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.
Admin Controls
Use f-admin-link when a theme needs to link admins to the site's deeper Frontbacked admin area. Frontbacked provides the admin root through $siteInfo; when none is provided, the link falls back to /fb-admin. Anchor elements default to opening in a new tab.
<a href="/fb-admin" f-admin-link target="_blank" rel="noopener noreferrer">Advanced Admin</a>
Use f-edit-mode-enable, f-edit-mode-disable, or f-edit-mode-toggle for page edit mode controls. The first enable in a browser shows Frontbacked's warning modal and stores the admin's acceptance in that browser.
<button type="button" f-edit-mode-enable f="true" f-show="$auth.admin.canEdit" f-hide="$editMode.active">
Enable edit mode
</button>
<button type="button" f-edit-mode-disable f="true" f-show="$auth.admin.canEdit && $editMode.active">
Exit edit mode
</button>
Support Launcher
Use f-support-launcher to render Frontbacked's reusable floating support button and widget. It reads visible WhatsApp and Telegram contacts from $siteInfo, shows contact choices when those channels exist, and falls back to the native site chat.
<div f-support-launcher="site-help"></div>
Use f-support-toggle only when a custom UI should toggle the native in-page chat directly instead of showing the launcher contact choices.
Custom links or buttons can open, close, or toggle the same launcher:
<button type="button" f-support-launcher-open="site-help">Contact us</button>
<button type="button" f-support-launcher-toggle="site-help">Support</button>
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.state.get("terms");
}
function termsLabel(value) {
return value ? "checked" : "unchecked";
}
Frontbacked.functions.define({ toggleTerms, 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 f-key="$.id">
<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-*. - Every non-settings item needs a stable
id. When the data uses another identity field, putf-key="expression"on the first-child template.
Frontbacked uses these keys to retain and reorder existing DOM nodes when list data changes. Missing and duplicate keys are reported as FQL errors.
<ul id="notifications" f="true" f-list="$state.notifications">
<li f-key="$.slug">
<span f="true" f-text="$.message"></span>
</li>
</ul>
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 site 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, $.body, f-attr-src="$.image", and f-attr-href="$.url", so decorative icons or fixed theme markup can stay in the theme.
Editable images
Bind an image to a settings field to give site admins a thumbnail with Replace image and Reset to default controls:
<img src="/assets/hero.jpg" alt="Our collection" f="true"
f-attr-src="$settings.home.hero.image">
Inside a settings-backed list, use f-attr-src="$.image". Image resize helpers can stay on the binding, such as $.image.resizeHeroSlideImage(). Nested settings lists work the same way. A video's f-attr-poster can use this image control too; it does not enable video uploads.
Admins see the image preview and its dimensions instead of editing its URL. Upload a JPEG, PNG, WebP, GIF, or AVIF image up to 5 MB and 40 million pixels. Replacements are saved as WebP images; animated inputs use a still frame. Videos, documents, SVG, and other files are not accepted, even if renamed as an image.
The replacement becomes part of the local draft. Use Save draft, then Save in the edit bar to publish it. Reset to default restores the image authored in the theme. Canceling an editor does not change the saved setting.
Image settings accept replacements uploaded for the current site and original images declared by the theme. They do not accept pasted external URLs or generic uploaded files. Text, alternative text, and links keep their usual text controls.
Settings list limits
Give a settings-backed component one direct child for each item its design supports. Three authored children allow up to three saved items. Text nodes and comments do not count. The first child remains the item template; all children provide the initial content.
You can set a smaller limit with data-frontbacked-settings-max-items. The effective maximum is the smallest of:
- The number of direct child elements authored in the component.
- A positive whole-number
data-frontbacked-settings-max-items, when provided. - 50 items, the maximum allowed for any settings array.
<section f="true" f-list="$settings.home.slides"
data-frontbacked-settings-max-items="2">
<article><h2 f="true" f-text="$.title">First slide</h2></article>
<article><h2>Second slide</h2></article>
<article><h2>Third slide</h2></article>
</section>
This component allows two saved slides. Without the attribute, it allows three. Setting the attribute to 100 still allows only three. To increase capacity, add authored children, up to the 50-item ceiling. Invalid attribute values do not remove the child-count limit.
The page editor disables Add item at the limit. Saves are also checked against the selected theme version's source, so changing the page in browser developer tools cannot raise the limit. An oversized save is rejected rather than silently shortened. Empty lists are allowed, and resetting a setting restores its theme defaults.
These limits also apply to $state selectors backed by $settings and to nested f-list="$.features" arrays inside a settings item. Each nested component has its own child count and optional maximum. If a page uses the same settings array in multiple components, its smallest declared limit applies when saving.
Save each declared setting through the page editor; replacing an entire parent settings object or saving a list as an object is not supported. For large or growing collections, use posts rather than settings lists.
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.