API reference

Relations & the embed widget

How relation fields resolve at read time, and how to fully customize the embed.

Relations at read time

When you fetch entries with ?populate=true, every relation field is resolved into the referenced entry's public shape. This is one level deep — a populated entry's own relations are never expanded further, so there is no unbounded fan-out.

Single relation

{
  "featuredProduct": {
    "id": "entry_zzz",
    "label": "The Chosen Product",
    "data": { "name": "The Chosen Product", "price": 49 },
    "publishedAt": "2026-08-01T00:00:00.000Z"
  }
}

Multiple relation

{
  "tags": [
    {
      "id": "entry_a",
      "label": "Fast",
      "data": { "name": "Fast" },
      "publishedAt": null
    },
    {
      "id": "entry_b",
      "label": "Cheap",
      "data": { "name": "Cheap" },
      "publishedAt": null
    }
  ]
}

The label is the referenced collection's titleField value, falling back to the first non-empty string in the data, and finally the entry id.

IMPORTANT

Population is best-effort. Only references to published, non-deleted target entries are resolved; a reference to an entry that was deleted, unpublished, or removed via its target collection being archived (which soft-deletes its entries) is silently omitted — never an error. The existence/ownership check happens at save time; read time only enriches what is validly published.

populated mirrors data

Inside a group field, populated is an array parallel to data[groupName], with relation sub-fields resolved the same way at any nesting depth. data itself is never mutated — populated is purely additive.

The embed widget in depth

embed.js is designed for arbitrary, owner-defined display fields, which is why it offers three escalating levels of control:

1. Default card grid

Without any markup, the widget renders every field with basic per-type formatting: images, a title, select/multiselect/tags as chips, URLs/emails as links, richtext inline, booleans as ✓/✗, and dates localized. relation values render as chips showing each referenced entry's label.

Use settings.titleField and settings.imageField on the collection to pick which fields are the card's heading and image; otherwise the first text and first image field are used.

2. Template override

Drop a <template data-vlozi-collection-item> inside the container to render exactly what you want:

<div data-vlozi-collection="col_abc123">
  <template data-vlozi-collection-item>
    <article>
      <img data-vlozi-field="photo" alt="" />
      <h3>{{name}}</h3>
      <span>{{role}}</span>
    </article>
  </template>
</div>
<script src="https://api.vlozi.app/collections/c/embed.js" async></script>
  • {{fieldName}} placeholders inside text are substituted per entry (single-pass, no loops).
  • Attributes [data-vlozi-field] bind element content (imgsrc, ahref, else text).
  • relation fields resolve to their populated label in templates (a single-value swap).
  • group fields are not resolvable in template mode — use the default card renderer for groups.

3. CSS restyling

Even without a template, every node carries stable hooks: vlzc-* classes plus data-vlozi-field (field name) and data-vlozi-entry (entry id) attributes. Restyle purely with CSS using your own stylesheet (the widget injects its minimal base CSS under #vlzc-css).

Views

View Set via Renders
grid default Responsive card grid
list data-vlozi-view="list" Vertical list
table data-vlozi-view="table" HTML table (headerless)

NOTE

The default table view renders fields sequentially as <td> cells inside each row (<tr>) but does not output a <thead> header. If structured column headers are required, either use a custom template inside the mount container or restyle via target classes.

Auto-mount & SPA hook

The widget auto-mounts every [data-vlozi-collection] container after DOMContentLoaded. It defers with a loading skeleton, shows a clear "content no longer available" message on missing data, and exposes a retry button on network failures.

For single-page apps that render containers after a route change, call it manually:

window.VloziCollections.mount(); // scan the whole document again
window.VloziCollections.mount(container); // mount one element

The widget fetches entries with populate=true automatically, so relation chips render out of the box.

Rendering every entry

The widget pages through the public /entries endpoint rather than fetching a single page of 100, so large collections render in full. It stops when it has fetched all published entries or hits the data-vlozi-max cap on the mount container (default 1000, minimum 50):

<div data-vlozi-collection="col_abc123" data-vlozi-max="300"></div>

Given a collection with, say, 250 published entries, the widget walks pages of 100 and renders all 250 (up to the cap). This matters for teams/testimonials/FAQ lists that grow past one page.

Collections · API referenceEdit on GitHub