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 attributeReal attribute
f-attr-hrefhref
f-attr-srcsrc
f-attr-altalt
f-attr-aria-checkedaria-checked
f-attr-data-statedata-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:

  1. The list root must have an id.
  2. The first child is used as the template.
  3. Inside the template, $ points to the current list item.
  4. Nested f="true" elements inside the template can use f-text and f-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.