Wix Velo repeaters explained: show any list of data with code
Mosaddik Billah 3 min read

On this page
A repeater is the Wix element for lists: product cards, team members, events, search results. You design one item, and Wix repeats it for every entry in your data. Connecting a repeater to a dataset needs no code, but as soon as you want data from an API, custom filtering, or a different look for some items, you’ll fill it with Velo. This guide covers the parts you need.
How a repeater thinks#
A repeater has two things:
- A template: the item you designed in the editor, with elements like
#title,#priceand#imageinside it. - Data: an array of objects, one per item. Each object must have a unique
_idas a string. Items from a CMS collection already have one.
You give it the data, and for each object it creates a copy of the template and calls your function to fill it in.
1. Fill a repeater with data#
$w.onReady(function () {
$w("#productsRepeater").onItemReady(($item, itemData, index) => {
$item("#title").text = itemData.name;
$item("#price").text = `$${itemData.price}`;
$item("#image").src = itemData.image;
});
$w("#productsRepeater").data = [
{ _id: "1", name: "Notebook", price: 12, image: "https://static.wixstatic.com/media/notebook.jpg" },
{ _id: "2", name: "Desk lamp", price: 45, image: "https://static.wixstatic.com/media/lamp.jpg" },
];
});Two details matter:
- Use
$item, not$w, insideonItemReady.$item("#title")is the title in this item.$w("#title")would target the title in every item at once. - Set
onItemReadybefore settingdata. The handler runs as items are created, so it has to exist first.
2. Load the data from a collection#
Most of the time the data comes from the CMS:
import wixData from "wix-data";
$w.onReady(async function () {
$w("#productsRepeater").onItemReady(($item, itemData) => {
$item("#title").text = itemData.name;
$item("#price").text = `$${itemData.price}`;
$item("#image").src = itemData.image;
});
const result = await wixData.query("Products")
.eq("inStock", true)
.ascending("name")
.limit(12)
.find();
$w("#productsRepeater").data = result.items;
});Data from an external API works the same way, as long as you add an _id to each object if it doesn’t have one:
const items = apiResults.map((row) => ({ ...row, _id: String(row.id) }));3. Show different things per item#
Because onItemReady runs once for each item, you can change each one based on its data:
$w("#productsRepeater").onItemReady(($item, itemData) => {
$item("#title").text = itemData.name;
if (itemData.onSale) {
$item("#saleBadge").show();
} else {
$item("#saleBadge").hide();
}
if (!itemData.description) {
$item("#description").collapse(); // removes the empty space too
}
});hide() keeps the element’s space in the layout; collapse() removes it, so the rest of the item moves up.
4. Handle a click inside an item#
A button inside a repeater is one element in the editor, so you add one click handler, outside onItemReady. The event tells you which item was clicked:
$w("#addButton").onClick((event) => {
const itemId = event.context.itemId;
const product = $w("#productsRepeater").data.find((item) => item._id === itemId);
const $item = $w.at(event.context);
$item("#addButton").label = "Added";
console.log("Add to list:", product.name);
});event.context.itemId is the clicked item’s _id, and $w.at(event.context) gives you the $item selector for that item, so you can change elements in it.
5. Update or filter the list#
Assigning a new array to data updates the repeater. Items with a new _id are created, missing ones are removed, and onItemReady runs for the new ones:
const all = result.items;
$w("#cheapOnly").onChange(() => {
$w("#productsRepeater").data = $w("#cheapOnly").checked
? all.filter((item) => item.price < 20)
: all;
});To refresh items that already exist after changing their data, call forEachItem, which runs a function for each current item:
$w("#productsRepeater").forEachItem(($item, itemData) => {
$item("#price").text = `$${itemData.price}`;
});For a full search box and dropdown built this way, see how to add search and category filters with Velo.
Common problems#
- Every item shows the same text: you used
$w(...)insideonItemReady. Use$item(...). - Nothing appears: an item without an
_id, or_idvalues that aren’t unique strings. - Clicks affect the wrong item: get the item from
event.context, not from a variable set earlier. - The repeater is also connected to a dataset: code and a dataset both filling the same repeater fight each other. Use one or the other.
If you need lists, catalogues or directories built on your Wix site, I do Wix Velo development.
Comments