Using ProcessWire's Repeater Fieldtype: A Practical Guide to Modular Content
Learn how ProcessWire's Repeater fieldtype lets editors create repeatable content blocks, how to access them via the API, and what pitfalls to watch for when nesting or scaling.
19 Oct 2025, 19:29 UTC

Problem: Editors Need Repeatable Blocks, Developers Need a Clean API
In many sites, content editors love the ability to add blocks of text, images, or custom content in any order they wish. Traditional ProcessWire pages are single entities, so a common workaround is to create separate pages for each block and link them together. That approach is cumbersome and hard to maintain. ProcessWire solves this with the Repeater fieldtype, which lets editors add a list of items that are stored as hidden child pages, but still behave like a single field when queried.
Thesis: The Repeater Is a Hidden Child-Page Store With a Simple API
Each Repeater item is a child page of the parent, invisible in the normal page tree. Because they are real pages, you can use the standard ProcessWire page API to query, sort, and iterate them. Editors get a friendly UI with drag-and-drop ordering, and developers get a consistent $page->repeater interface. The trick is to set it up correctly and be aware of performance and migration quirks.
1. Setting Up a Repeater Field
- In the admin, go to Setup > Fields and click New Field.
- Choose Repeater as the field type, give it a name (e.g.,
hero_blocks), and save. - In the field settings you can define:
- Min/Max items – enforce how many blocks may exist.
- Drag-and-drop – enable the UI reorder control.
- Sub-fields – add the fields each block should contain (e.g.,
title,image,body).
- Attach the field to the desired template. When you edit a page of that template, the Repeater appears as a collapsible section where editors can add, delete, and reorder items.
2. Accessing Repeater Items in Templates
Because each item is a child page, you can iterate over them just like normal pages. Run this in a template file (no special permissions needed beyond normal template editing):
// Count items
$cnt = count($page->hero_blocks);
echo "There are " . $cnt . " hero blocks.";
// Iterate with foreach
foreach ($page->hero_blocks as $block) {
echo "<h2>" . $block->title . "</h2>";
if ($block->image) echo $block->image->url;
echo "<p>" . $block->body . "</p>";
}
Alternatively, use find() to apply filters or explicit sorting:
// Get items sorted by the internal sort column
$blocks = $page->hero_blocks->find('sort=sort');
foreach ($blocks as $block) {
// render
}
The API is identical to working with any other page set, which keeps templates clean.
3. Nested Repeaters and RepeaterMatrix
Repeater items can themselves contain another Repeater field, useful for a “section → rows → columns” hierarchy. The admin UI shows the nested list automatically, and drag-and-drop can be enabled at each level without extra configuration:
foreach ($page->hero_blocks as $section) {
foreach ($section->rows as $row) {
foreach ($row->columns as $col) {
// render column content
}
}
}
If you need different layouts per item, the RepeaterMatrix extension lets you predefine multiple field layouts (matrix types) and switch between them per item, giving a block-based editor experience similar to Gutenberg.
4. Performance Considerations and Migration Caveats
- Database load: every Repeater item is a row in the
pagestable. Large collections or deep nesting can slow queries on high-traffic sites. Mitigate by limiting item counts or caching rendered output. - Changing the fieldtype: swapping a Repeater for another fieldtype after content exists requires a migration script – the underlying child-page template cannot be altered through the admin once items exist.
- Stored order: drag-and-drop writes to the child pages'
sortcolumn; the saved order persists even if you later disable the UI control.
Concrete Example: Rendering a Hero Section
Suppose hero_blocks has subfields title (text), image (image), and caption (textarea). This fragment renders each block as a banner:
foreach ($page->hero_blocks as $block) {
echo "<section class='hero'>";
if ($block->image) {
echo "<img src='" . $block->image->url . "' alt='" . $block->title . "'>";
}
echo "<h1>" . $block->title . "</h1>";
echo "<p>" . $block->caption . "</p>";
echo "</section>";
}
After adding three items in the admin, the output contains three <section> elements in the order the editor arranged them.
Trade-off: Deep Nesting vs. Simplicity
Nested repeaters are powerful but add complexity in both the admin and the database. For simple block lists, a single Repeater is usually enough. If you anticipate thousands of items, consider RepeaterMatrix, flattening the structure into separate templates linked by page fields, or caching the rendered output.
Actionable Closing: Verify Your Setup
- Create a test page and add a few Repeater items.
- Output
count($page->hero_blocks)in the template to verify the count matches. - Render the items with the code above and confirm the markup.
- Reorder items in the admin, then check that
$page->hero_blocks->find('sort=sort')returns the new sequence. - Inspect the page tree (with hidden pages visible) or the
pagestable to confirm the child-page structure.
Follow these steps and you will have an editor-friendly, maintainable modular content system.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.