mosaddik.dev
All posts

Wix Velo repeaters explained: show any list of data with code

Mosaddik Billah 3 min read

Wix Velo

A wall of identical small blue square tiles in a grid
Photo by Lazy_camera_girl on Unsplash
On this page
  1. How a repeater thinks
  2. 1. Fill a repeater with data
  3. 2. Load the data from a collection
  4. 3. Show different things per item
  5. 4. Handle a click inside an item
  6. 5. Update or filter the list
  7. Common problems

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, #price and #image inside it.
  • Data: an array of objects, one per item. Each object must have a unique _id as 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#

JavaScript
$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, inside onItemReady. $item("#title") is the title in this item. $w("#title") would target the title in every item at once.
  • Set onItemReady before setting data. 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:

JavaScript
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:

JavaScript
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:

JavaScript
$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:

JavaScript
$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:

JavaScript
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:

JavaScript
$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(...) inside onItemReady. Use $item(...).
  • Nothing appears: an item without an _id, or _id values 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