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 (img→src,a→href, else text). relationfields resolve to their populated label in templates (a single-value swap).groupfields 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 elementThe 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.