Description: The x-for directive in AlpineJS allows you to create dynamic lists and repeating blocks of HTML by looping through arrays, objects, or a range of numbers in your component's data. This is very similar to how you might use {% for item in items %}...{% endfor %} in Django or Flask templates to render lists.
The x-for directive is powerful for rendering collections of data. Here are its common uses:
This is the most common use case. You provide an array from your component's data, and Alpine will repeat the template for each item in the array.
<template x-for="item in items" :key="item.id">
<div>
<span x-text="item.name"></span>
</div>
</template>
Here, items would be an array in your x-data, like [{ id: 1, name: 'Product A' }, { id: 2, name: 'Product B' }]. The item variable becomes available inside the template for each iteration.
Sometimes, you might need the current index of the loop. You can get this by specifying a second variable in the x-for expression.
<template x-for="(item, index) in items" :key="item.id ?? index">
<div>
<span x-text="index + 1"></span>:
<span x-text="item.name"></span>
</div>
</template>
Note on :key: While you can use index as a key, it's generally recommended to use a unique identifier from the data itself (like item.id) if available, especially for lists that can change dynamically (items added, removed, or reordered).
If you need to repeat a block of HTML a fixed number of times, you can iterate over a range.
<template x-for="i in 5" :key="i">
<div>Iteration number <span x-text="i"></span></div>
</template>
This will repeat the <div> 5 times, with i taking values from 1 to 5 (inclusive).
:key:
The :key attribute is crucial when using x-for. AlpineJS uses the key to identify each DOM element created by the loop. This allows Alpine to efficiently update, reorder, add, or remove elements when the underlying data array changes.
item.id, product.uuid).index as a key can be problematic if the list items can be reordered, or if items can be inserted/deleted from the middle of the list, as this can lead to incorrect component state or rendering issues. Use index as a key only for static lists or if no other unique identifier is available.:key or using a non-unique key:
As mentioned, :key is vital. Forgetting it, or using a non-unique key (like the loop index if list items can be reordered or removed from arbitrary positions), can cause AlpineJS to struggle with efficiently updating the DOM. This might manifest as elements not updating correctly, focus being lost, or other unexpected behaviors. Always strive to use a unique ID from your data source for the :key (e.g., product.id from your database).
x-for must be on a <template> tag:
Similar to the x-if directive, x-for must be placed on a <template> HTML tag. AlpineJS will then use the content inside this <template> tag as the blueprint for each item it creates during the loop. The <template> tag itself will not be rendered in the final DOM; only its contents, repeated for each iteration.
<!-- Correct -->
<template x-for="item in items" :key="item.id">
<div x-text="item.name"></div>
</template>
<!-- Incorrect -->
<div x-for="item in items" :key="item.id" x-text="item.name"></div>
item) directly to update the original array:
When you loop with x-for="item in items", the item variable within each iteration is essentially a copy or a reference for that specific iteration's scope. Modifying properties of item (if item is an object) will reflect in the original array if it's a reference type, but reassigning item itself (e.g., item = null) will not remove it from the original items array in your x-data. To add, remove, or replace items in the list, you must modify the source array (this.items) directly, typically within a method called via an event listener (e.g., x-on:click="removeItem(item.id)").
For example, to remove an item:
// In Alpine.data component:
removeItem(itemId) {
this.items = this.items.filter(i => i.id !== itemId);
}
Loading items...