The x-for directive in AlpineJS is your primary tool for dynamically rendering lists and repeating blocks of HTML. It allows you to iterate over arrays or a range of numbers directly within your component's template, similar to how you might use {% for item in items %} in Django or Flask templates, or v-for in Vue.js.
This is essential for displaying collections of data, such as user lists, product cards, table rows, or any scenario where you need to generate multiple similar HTML structures based on a dataset.
This is the most common use case. You typically have an array of objects in your component's data, and you want to render a piece of HTML for each object.
<!-- Assuming 'items' is an array like [{id: 1, name: 'Alpha'}, {id: 2, name: 'Beta'}] -->
<ul>
<template x-for="item in items" :key="item.id">
<li x-text="item.name"></li>
</template>
</ul>
x-for="item in items": Iterates over the items array. In each iteration, the current object is available as item.:key="item.id": The :key attribute is crucial. It provides AlpineJS with a unique identifier for each item in the list. This allows Alpine to efficiently update, reorder, add, or remove elements without losing state or recreating unaffected parts of the DOM. Always use a truly unique property from your item (like a database ID) as the key.Sometimes, you might need access to the current item's index within the array.
<!-- Assuming 'items' is an array ['First', 'Second', 'Third'] -->
<ol>
<template x-for="(item, index) in items" :key="index">
<li>
<span x-text="index + 1"></span>. <span x-text="item"></span>
</li>
</template>
</ol>
x-for="(item, index) in items": Here, item is the current element, and index is its 0-based position in the array.:key="index": Using the index as a key is acceptable if the list is static (items are not reordered, added, or removed from the middle) AND items do not have their own unique IDs. If unique IDs are available on items, prefer :key="item.id" even when using the index for display.You can also use x-for to repeat a block of HTML a fixed number of times.
<div>
<template x-for="i in 5" :key="i">
<!-- This div will be rendered 5 times -->
<div>Iteration #<span x-text="i"></span></div>
</template>
</div>
x-for="i in 5": This will loop 5 times. The variable i will take values from 1 to 5 (inclusive).:key="i": In this case, i itself is a suitable key as it's unique for each iteration.:key:The :key attribute is vital for performance and stability when dealing with dynamic lists. When the data array changes (items added, removed, or reordered), AlpineJS uses the keys to identify which DOM elements correspond to which data items.
Without keys, or with non-unique keys, Alpine might have to re-render the entire list or might incorrectly update elements, leading to bugs, lost input focus, or poor performance. Always strive to use a unique identifier from your data item (e.g., item.id) as the key.
:key or using a non-unique key:
This is a frequent source of issues. AlpineJS relies on :key to efficiently patch the DOM. If you omit it, Alpine will warn you in the console. If you use a non-unique key (like the loop index when the list can be reordered or items removed from the middle), you can encounter strange behavior: elements might not update correctly, component state within list items might get mixed up, or performance can degrade. Always use a unique ID from your data item as the key whenever possible.
x-for must be on a <template> tag:
Similar to x-if, the x-for directive should almost always be placed on a <template> tag. The <template> tag itself is not rendered in the DOM; its content is used as the blueprint for each iteration. If you put x-for directly on an element like <div x-for="item in items">, that <div> itself (along with its contents) will be repeated. While this might seem to work for simple cases, using <template> is the standard and more flexible approach, especially when you need to repeat multiple sibling elements per item or use x-for on table rows (<tr>).
item) directly to update the original array:
In Python, if item is a mutable object (like a dictionary) in a loop for item in items:, modifying item['key'] often modifies the original object in the list. In AlpineJS, while direct property mutations like item.someProperty = 'newValue' within an event handler might reflect in the UI (because item is a reference to an object in the items array), it's generally safer and clearer to perform data manipulations on the source array (this.items) within your component's methods. For example, to update an item, you might do this.items[index].property = 'new value' or replace the item entirely: this.items.splice(index, 1, newItem). Reassigning the loop variable itself (e.g., item = anotherObject) within the template's scope will not update the original items array.
This table is populated from data (initially simulated as fetched from a server) and demonstrates adding and removing items. Notice how :key is used on user.id.
| ID | Name | Role | Actions |
|---|---|---|---|
| No users to display. Initial data is loading or empty. | |||
Number of users:
This demonstrates iterating a fixed number of times (e.g., i in 3).