Skip to main content
Version: 3.0

Repeater

Overview

The repeater component allows you to output a JSON array of repeated form components.

use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;

Repeater::make('members')
->schema([
TextInput::make('name')->required(),
Select::make('role')
->options([
'member' => 'Member',
'administrator' => 'Administrator',
'owner' => 'Owner',
])
->required(),
])
->columns(2)
Repeater

We recommend that you store repeater data with a JSON column in your database. Additionally, if you're using Eloquent, make sure that column has an array cast.

As evident in the above example, the component schema can be defined within the schema() method of the component:

use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\TextInput;

Repeater::make('members')
->schema([
TextInput::make('name')->required(),
// ...
])

If you wish to define a repeater with multiple schema blocks that can be repeated in any order, please use the builder.

Setting empty default items

Repeaters may have a certain number of empty items created by default, using the defaultItems() method:

use Filament\Forms\Components\Repeater;

Repeater::make('members')
->schema([
// ...
])
->defaultItems(3)

Note that these default items are only created when the form is loaded without existing data. Inside panel resources this only works on Create Pages, as Edit Pages will always fill the data from the model.

Adding items

An action button is displayed below the repeater to allow the user to add a new item.

Setting the add action button's label

You may set a label to customize the text that should be displayed in the button for adding a repeater item, using the addActionLabel() method:

use Filament\Forms\Components\Repeater;

Repeater::make('members')
->schema([
// ...
])
->addActionLabel('Add member')

Preventing the user from adding items

You may prevent the user from adding items to the repeater using the addable(false) method:

use Filament\Forms\Components\Repeater;

Repeater::make('members')
->schema([
// ...
])
->addable(false)

Deleting items

An action button is displayed on each item to allow the user to delete it.

Preventing the user from deleting items

You may prevent the user from deleting items from the repeater using the deletable(false) method:

use Filament\Forms\Components\Repeater;

Repeater::make('members')
->schema([
// ...
])
->deletable(false)

Reordering items

A button is displayed on each item to allow the user to drag and drop to reorder it in the list.

Preventing the user from reordering items

You may prevent the user from reordering items from the repeater using the reorderable(false) method:

use Filament\Forms\Components\Repeater;

Repeater::make('members')
->schema([
// ...
])
->reorderable(false)

Reordering items with buttons

You may use the reorderableWithButtons() method to enable reordering items with buttons to move the item up and down:

use Filament\Forms\Components\Repeater;

Repeater::make('members')
->schema([
// ...
])
->reorderableWithButtons()
Repeater that is reorderable with buttons

Preventing reordering with drag and drop

You may use the reorderableWithDragAndDrop(false) method to prevent items from being ordered with drag and drop:

use Filament\Forms\Components\Repeater;

Repeater::make('members')
->schema([
// ...
])
->reorderableWithDragAndDrop(false)

Collapsing items

The repeater may be collapsible() to optionally hide content in long forms:

use Filament\Forms\Components\Repeater;

Repeater::make('qualifications')
->schema([
// ...
])
->collapsible()

You may also collapse all items by default:

use Filament\Forms\Components\Repeater;

Repeater::make('qualifications')
->schema([
// ...
])
->collapsed()
Collapsed repeater

Cloning items

You may allow repeater items to be duplicated using the cloneable() method:

use Filament\Forms\Components\Repeater;

Repeater::make('qualifications')
->schema([
// ...
])
->cloneable()
Cloneable repeater

Integrating with an Eloquent relationship

If you're building a form inside your Livewire component, make sure you have set up the form's model. Otherwise, Filament doesn't know which model to use to retrieve the relationship from.

You may employ the relationship() method of the Repeater to configure a HasMany relationship. Filament will load the item data from the relationship, and save it back to the relationship when the form is submitted. If a custom relationship name is not passed to relationship(), Filament will use the field name as the relationship name:

use Filament\Forms\Components\Repeater;

Repeater::make('qualifications')
->relationship()
->schema([
// ...
])

Reordering items in a relationship

By default, reordering relationship repeater items is disabled. This is because your related model needs an sort column to store the order of related records. To enable reordering, you may use the orderColumn() method, passing in a name of the column on your related model to store the order in:

use Filament\Forms\Components\Repeater;

Repeater::make('qualifications')
->relationship()
->schema([
// ...
])
->orderColumn('sort')

If you use something like spatie/eloquent-sortable with an order column such as order_column, you may pass this in to orderColumn():

use Filament\Forms\Components\Repeater;

Repeater::make('qualifications')
->relationship()
->schema([
// ...
])
->orderColumn('order_column')

Integrating with a BelongsToMany Eloquent relationship

There is a common misconception that using a BelongsToMany relationship with a repeater is as simple as using a HasMany relationship. This is not the case, as a BelongsToMany relationship requires a pivot table to store the relationship data. The repeater saves its data to the related model, not the pivot table. Therefore, if you want to map each repeater item to a row in the pivot table, you must use a HasMany relationship with a pivot model to use a repeater with a BelongsToMany relationship.

Imagine you have a form to create a new Order model. Each order belongs to many Product models, and each product belongs to many orders. You have a order_product pivot table to store the relationship data. Instead of using the products relationship with the repeater, you should create a new relationship called orderProducts on the Order model, and use that with the repeater:

use Illuminate\Database\Eloquent\Relations\HasMany;

public function orderProducts(): HasMany
{
return $this->hasMany(OrderProduct::class);
}

If you don't already have an OrderProduct pivot model, you should create that, with inverse relationships to Order and Product:

use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\Pivot;

class OrderProduct extends Pivot
{
public function order(): BelongsTo
{
return $this->belongsTo(Order::class);
}

public function product(): BelongsTo
{
return $this->belongsTo(Product::class);
}
}

Now you can use the orderProducts relationship with the repeater, and it will save the data to the order_product pivot table:

use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\Select;

Repeater::make('orderProducts')
->relationship()
->schema([
Select::make('product_id')
->relationship('product', 'name')
->required(),
// ...
])

You may mutate the data for a related item before it is filled into the field using the mutateRelationshipDataBeforeFillUsing() method. This method accepts a closure that receives the current item's data in a $data variable. You must return the modified array of data:

use Filament\Forms\Components\Repeater;

Repeater::make('qualifications')
->relationship()
->schema([
// ...
])
->mutateRelationshipDataBeforeFillUsing(function (array $data): array {
$data['user_id'] = auth()->id();

return $data;
})

You may mutate the data for a new related item before it is created in the database using the mutateRelationshipDataBeforeCreateUsing() method. This method accepts a closure that receives the current item's data in a $data variable. You must return the modified array of data:

use Filament\Forms\Components\Repeater;

Repeater::make('qualifications')
->relationship()
->schema([
// ...
])
->mutateRelationshipDataBeforeCreateUsing(fn (array $data): array {
$data['user_id'] = auth()->id();

return $data;
})

You may mutate the data for an existing related item before it is saved in the database using the mutateRelationshipDataBeforeSaveUsing() method. This method accepts a closure that receives the current item's data in a $data variable. You must return the modified array of data:

use Filament\Forms\Components\Repeater;

Repeater::make('qualifications')
->relationship()
->schema([
// ...
])
->mutateRelationshipDataBeforeSaveUsing(fn (array $data): array {
$data['user_id'] = auth()->id();

return $data;
})

Grid layout

You may organize repeater items into columns by using the grid() method:

use Filament\Forms\Components\Repeater;

Repeater::make('qualifications')
->schema([
// ...
])
->grid(2)
Repeater with a 2 column grid of items

This method accepts the same options as the columns() method of the grid. This allows you to responsively customize the number of grid columns at various breakpoints.

Adding a label to repeater items based on their content

You may add a label for repeater items using the itemLabel() method. This method accepts a closure that receives the current item's data in a $state variable. You must return a string to be used as the item label:

use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Select;

Repeater::make('members')
->schema([
TextInput::make('name')
->required()
->blur(),
Select::make('role')
->options([
'member' => 'Member',
'administrator' => 'Administrator',
'owner' => 'Owner',
])
->required(),
])
->columns(2)
->itemLabel(fn (array $state): ?string => $state['name'] ?? null),

Any fields that you use from $state should be live() if you wish to see the item label update live as you use the form.

Repeater with item labels

Using $get() to access parent field values

All form components are able to use $get() and $set() to access another field's value. However, you might experience unexpected behaviour when using this inside the repeater's schema.

This is because $get() and $set(), by default, are scoped to the current repeater item. This means that you are able to interact with another field inside that repeater item easily without knowing which repeater item the current form component belongs to.

The consequence of this is that you may be confused when you are unable to interact with a field outside the repeater. We use ../ syntax to solve this problem - $get('../../parent_field_name').

Consider your form has this data structure:

[
'client_id' => 1,

'repeater' => [
'item1' => [
'service_id' => 2,
],
],
]

You are trying to retrieve the value of client_id from inside the repeater item.

$get() is relative to the current repeater item, so $get('client_id') is looking for $get('repeater.item1.client_id').

You can use ../ to go up a level in the data structure, so $get('../client_id') is $get('repeater.client_id') and $get('../../client_id') is $get('client_id').

Repeater validation

As well as all rules listed on the validation page, there are additional rules that are specific to repeaters.

Number of items validation

You can validate the minimum and maximum number of items that you can have in a repeater by setting the minItems() and maxItems() methods:

use Filament\Forms\Components\Repeater;

Repeater::make('members')
->schema([
// ...
])
->minItems(2)
->maxItems(5)

Customizing the repeater action objects

This field uses action objects for easy customization of buttons within it. You can customize these buttons by passing a function to an action registration method. The function has access to the $action object, which you can use to customize it. The following methods are available to customize the actions:

  • addAction()
  • cloneAction()
  • collapseAction()
  • collapseAllAction()
  • deleteAction()
  • expandAction()
  • expandAllAction()
  • moveDownAction()
  • moveUpAction()
  • reorderAction()

Here is an example of how you might customize an action:

use Filament\Forms\Components\Actions\Action;
use Filament\Forms\Components\Repeater;

Repeater::make('members')
->schema([
// ...
])
->collapseAllAction(
fn (Action $action) => $action->label('Collapse all members'),
)

Confirming repeater actions with a modal

You can confirm actions with a modal by using the requiresConfirmation() method on the action object. You may use any modal customization method to change its content and behaviour:

use Filament\Forms\Components\Actions\Action;
use Filament\Forms\Components\Repeater;

Repeater::make('members')
->schema([
// ...
])
->deleteAction(
fn (Action $action) => $action->requiresConfirmation(),
)

The collapseAction(), collapseAllAction(), expandAction(), expandAllAction() and reorderAction() methods do not support confirmation modals, as clicking their buttons does not make the network request that is required to show the modal.