jQuery Repeater: Repeatable Form Fields & Nested Groups
| File Size: | 50.7 KB |
|---|---|
| Views Total: | 12686 |
| Last Update: | |
| Publish Date: | |
| Official Website: | Go to website |
| License: | MIT |
jQuery Repeater is a jQuery plugin that creates repeatable form field groups and rewrites each field name into an indexed structure for server-side submission.
The plugin handles add and remove controls, nested repeaters, default values, existing record data, callbacks, and structured JavaScript output. It's ideal for invoice lines, contacts, addresses, product variants, employment records, and other forms that accept an unknown number of rows.
Features:
- Repeatable groups with add and remove controls.
- Automatic indexed field names for server-side form parsing.
- Nested repeaters for hierarchical form data.
- Default values for newly created items.
- Programmatic data loading through
setList(). - Structured JavaScript output through
repeaterVal(). show,hide, andreadycallbacks.- Text fields, textareas, selects, checkboxes, radio buttons, date fields, file inputs, and other standard form controls.
How to use it:
1. Download and include the minified version of the repeater plugin after jQuery.
<script src="/path/to/cdn/jquery.min.js"></script> <script src="/path/to/jquery.repeater.min.js"></script>
2. Place one template row inside an element with data-repeater-list. Each repeatable row needs data-repeater-item. Add and remove controls use data-repeater-create and data-repeater-delete.
| Attribute | Purpose |
|---|---|
data-repeater-list="items" |
Defines the root name for the repeated array and contains the repeatable items. |
data-repeater-item |
Marks one repeatable row. The first matching item becomes the clone template. |
data-repeater-create |
Marks a button or clickable element that creates a new item. |
data-repeater-delete |
Marks a button or clickable element that removes its closest repeater item. |
<form class="invoice-form" action="/invoices/save" method="post">
<div class="invoice-repeater">
<div data-repeater-list="items">
<div data-repeater-item>
<label>
Description
<input type="text" name="description" required>
</label>
<label>
Quantity
<input type="number" name="quantity" min="1" value="1">
</label>
<label>
Unit price
<input type="number" name="unit_price" min="0" step="0.01">
</label>
<button type="button" data-repeater-delete>Remove line</button>
</div>
</div>
<button type="button" data-repeater-create>Add line</button>
</div>
<button type="submit">Save invoice</button>
</form>
3. Initialize the plugin on the form. The first data-repeater-item becomes the template for every new row. Repeater clears copied values, applies matching entries from defaultValues, updates field names, and then runs the show callback.
$('.invoice-repeater').repeater({
defaultValues: {
quantity: 1
},
show: function () {
$(this).slideDown(150);
},
hide: function (deleteElement) {
if (confirm('Remove this invoice line?')) {
$(this).slideUp(150, deleteElement);
}
}
});
The value of data-repeater-list becomes the root key. A field named description receives these names as rows are added:
items[0][description] items[1][description] items[2][description]
Checkbox groups and multiple selects receive an additional array suffix:
items[0][taxes][]
The plugin recalculates indexes after an item is added or removed. Keep the original names inside the template short, such as description or quantity. Do not write the indexed form manually.
4. Start With An Empty List. Keep one hidden template item in the markup and set initEmpty to true. The hidden row still acts as the template. The plugin removes it during initialization and waits for the create control.
<div class="contact-repeater">
<div data-repeater-list="contacts">
<div data-repeater-item style="display: none;">
<input type="text" name="name" placeholder="Contact name">
<input type="email" name="email" placeholder="Email address">
<button type="button" data-repeater-delete>Remove</button>
</div>
</div>
<button type="button" data-repeater-create>Add contact</button>
</div>
$('.contact-repeater').repeater({
initEmpty: true
});
5. Load Existing Records With setList(). It replaces the current rows with data from an array. Object keys must match the original field names inside the template. This method works well on edit screens that receive existing records from a server. It removes every current repeater item before it creates the new list.
var $contacts = $('.contact-repeater').repeater({
initEmpty: true
});
$contacts.setList([
{
name: 'Avery Chen',
email: '[email protected]'
},
{
name: 'Jordan Lee',
email: '[email protected]'
}
]);
6. Read structured values with repeaterVal(). Use normal form submission when your server already parses bracketed field names. Use repeaterVal() when JavaScript needs the complete repeated data before submission.
var repeaterData = $('.contact-repeater').repeaterVal();
console.log(repeaterData);
The result follows the nested structure created by data-repeater-list:
{
contacts: [
{
name: 'Avery Chen',
email: '[email protected]'
},
{
name: 'Jordan Lee',
email: '[email protected]'
}
]
}
7. The plugin also supports nested form fields. Nested repeaters need their own list, item, create control, delete control, and selector configuration. A nested SKU field receives a name such as products[0][variants][0][sku]. Nested configuration accepts the same options as the outer repeater and requires a selector.
<div class="product-repeater">
<div data-repeater-list="products">
<div data-repeater-item>
<input type="text" name="product_name" placeholder="Product name">
<button type="button" data-repeater-delete>Remove product</button>
<div class="variant-repeater">
<div data-repeater-list="variants">
<div data-repeater-item>
<input type="text" name="sku" placeholder="SKU">
<input type="number" name="price" min="0" step="0.01">
<button type="button" data-repeater-delete>Remove variant</button>
</div>
</div>
<button type="button" data-repeater-create>Add variant</button>
</div>
</div>
</div>
<button type="button" data-repeater-create>Add product</button>
</div>
$('.product-repeater').repeater({
repeaters: [
{
selector: '.variant-repeater',
defaultValues: {
price: '0.00'
}
}
]
});
8. Reindex rows after Drag And Drop. The plugin does not include sorting. The ready callback exposes a function that recalculates field indexes after another library reorders the rows.
var refreshRepeaterIndexes;
$('.invoice-repeater').repeater({
ready: function (setIndexes) {
refreshRepeaterIndexes = setIndexes;
}
});
// Run this after your sorting library finishes moving rows.
refreshRepeaterIndexes();
9. Initialize Controls Inside New Rows. Third-party selects, date pickers, masks, and editors often store state outside the raw form element. Initialize those controls from show after Repeater inserts the new row.
function initializeDynamicControls(item) {
var $item = $(item);
// Add project-specific control initialization here.
$item.find('[data-dynamic-control]').each(function () {
initializeControl(this);
});
}
$('.profile-repeater').repeater({
show: function () {
initializeDynamicControls(this);
$(this).slideDown(150);
}
});
Use delegated event handlers for controls that do not require per-row initialization:
$('.profile-repeater').on('change', '[name]', function () {
console.log('Repeated field changed:', this.name, this.value);
});
All plugin options
| Option | Description |
|---|---|
initEmpty |
Boolean. Default: false. Removes the initial item during setup and starts with no visible rows. Keep one hidden item in the HTML as the template. |
defaultValues |
Object. Default: not set. Assigns values to fields in each newly created item. Keys match the original field names. |
show |
Function. Default: immediate show(). Runs after a new hidden item enters the list. Inside the callback, this refers to the new item element. |
hide |
Function. Default: immediate removal. Runs after a delete control is clicked. The first argument removes the item and reindexes the remaining rows. |
ready |
Function. Default: not set. Runs after initialization and receives setIndexes, which recalculates names after external row reordering. |
isFirstItemUndeletable |
Boolean. Default: false. Removes the delete control from the first item during initialization. |
repeaters |
Array. Default: not set. Defines nested repeaters. Each nested configuration requires a selector and accepts the other Repeater options. |
API Methods:
| Method | Description |
|---|---|
$(selector).repeater(options) |
Initializes the plugin. It returns the jQuery collection and attaches the setList method. |
$repeater.setList(rows) |
Removes the current items and rebuilds the list from an array of row objects. Nested list keys can contain arrays of nested row objects. |
$(selector).repeaterVal() |
Reads rewritten field names and returns a structured object or array that matches the repeated form hierarchy. |
Callbacks And Integration Hooks:
| Callback | Runs When |
|---|---|
show() |
A new item has entered the list but remains hidden. this refers to the item element. |
hide(deleteElement) |
A delete control has been clicked. Call deleteElement() after confirmation, animation, or an application-specific delete request. |
ready(setIndexes) |
The repeater has completed initialization. Save or bind setIndexes when another script can change row order. |
jQuery Repeater does not publish custom jQuery events. Use its callbacks for item lifecycle work and delegated DOM events for repeated input changes.
Supported Form Controls:
- Text, password, email, URL, search, telephone, and hidden inputs.
- Number, range, color, date, datetime-local, month, time, and week inputs.
- Textareas and single selects.
- Multiple selects, checkboxes, and radio groups.
- Single and multiple file inputs.
- Untyped inputs, which use the text input handler.
Alternatives And Related Resources
- Repeatable Form Fields With Add/Remove Capabilities - jQuery Repeater
- Duplicate, Remove, and Sort Rows In Forms - jQuery formRowRepeater
- jQuery Plugin To Dynamically Add More Form Fields - czMore
- Duplicate Input Fields With Add/Remove Buttons - jQuery repeatable.js
FAQs
Q: Does jQuery Repeater work with jQuery 4?
A: The unmodified plugin does not. Its source calls $.isArray(), which jQuery 4 removes. Use jQuery 3.7.1 or patch that call to Array.isArray() and test the complete form.
Q: Why does the plugin change every field name?
A: Repeated controls would otherwise submit duplicate flat names. Repeater converts them into indexed names such as items[0][description], which preserves each row in the submitted data.
Q: How do I edit records that already exist?
A: Initialize the repeater, save the returned jQuery object, and call setList() with an array of objects. Each object key must match a field name from the item template.
Q: Can I limit how many rows a user adds?
A: The plugin has no minimum or maximum option. Add that rule in your own create and delete control logic or choose a repeater plugin with count limits.
Q: How do I validate newly added fields?
A: Use native constraints or a validation plugin that supports dynamic controls. Attach per-field rules inside show, or use delegated validation logic on the repeater container.
Changelog:
2026-08-01
- Updated doc
This awesome jQuery plugin is developed by DubFriend. For more Advanced Usages, please check the demo page or visit the official website.











