Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Forms Laravel Package

filament/forms

Filament Forms is a Laravel package for building powerful, reactive admin forms with a fluent, component-based API. Create fields, layouts, validation, conditional logic, and dynamic interactions quickly, with tight Livewire integration and great DX for panels and apps.

View on GitHub
Deep Wiki
Context7

title: Repeater

import Aside from "@components/Aside.astro" import AutoScreenshot from "@components/AutoScreenshot.astro" import UtilityInjection from "@components/UtilityInjection.astro"

Introduction

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)

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. The default is only used when a schema is loaded with no data. In a standard panel resource, defaults are used on the Create page, not the Edit page. To use default items, pass the number of items to the defaultItems() method:

use Filament\Forms\Components\Repeater;

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

<UtilityInjection set="formFields" version="5.x">As well as allowing a static value, the defaultItems() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.</UtilityInjection>

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')

<UtilityInjection set="formFields" version="5.x">As well as allowing a static value, the addActionLabel() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.</UtilityInjection>

Aligning the add action button

By default, the add action is aligned in the center. You may adjust this using the addActionAlignment() method, passing an Alignment option of Alignment::Start or Alignment::End:

use Filament\Forms\Components\Repeater;
use Filament\Support\Enums\Alignment;

Repeater::make('members')
    ->schema([
        // ...
    ])
    ->addActionAlignment(Alignment::Start)

<UtilityInjection set="formFields" version="5.x">As well as allowing a static value, the addActionAlignment() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.</UtilityInjection>

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)

<UtilityInjection set="formFields" version="5.x">As well as allowing a static value, the addable() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.</UtilityInjection>

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)

<UtilityInjection set="formFields" version="5.x">As well as allowing a static value, the deletable() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.</UtilityInjection>

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)

<UtilityInjection set="formFields" version="5.x">As well as allowing a static value, the reorderable() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.</UtilityInjection>

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()

Optionally, you may pass a boolean value to control if the repeater should be ordered with buttons or not:

use Filament\Forms\Components\Repeater;

Repeater::make('members')
    ->schema([
        // ...
    ])
    ->reorderableWithButtons(FeatureFlag::active())

<UtilityInjection set="formFields" version="5.x">As well as allowing a static value, the reorderableWithButtons() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.</UtilityInjection>

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)

<UtilityInjection set="formFields" version="5.x">As well as allowing a static value, the reorderableWithDragAndDrop() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.</UtilityInjection>

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()

Optionally, the collapsible() and collapsed() methods accept a boolean value to control if the repeater should be collapsible and collapsed or not:

use Filament\Forms\Components\Repeater;

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

<UtilityInjection set="formFields" version="5.x">As well as allowing static values, the collapsible() and collapsed() methods also accept functions to dynamically calculate it. You can inject various utilities into the function as parameters.</UtilityInjection>

Cloning items

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

use Filament\Forms\Components\Repeater;

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

Optionally, the cloneable() method accepts a boolean value to control if the repeater should be cloneable or not:

use Filament\Forms\Components\Repeater;

Repeater::make('qualifications')
    ->schema([
        // ...
    ])
    ->cloneable(FeatureFlag::active())

<UtilityInjection set="formFields" version="5.x">As well as allowing a static value, the cloneable() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.</UtilityInjection>

Integrating with an Eloquent relationship

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 a 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')

<UtilityInjection set="formFields" version="5.x">As well as allowing a static value, the orderColumn() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.</UtilityInjection>

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 $incrementing = true;

    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(),
        // ...
    ])

Mutating related item data before filling the field

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;
    })

<UtilityInjection set="formFields" version="5.x" extras="Data;;array<array<string, mixed>>;;$data;;The data that is being filled into the repeater.">You can inject various utilities into the function passed to mutateRelationshipDataBeforeFillUsing() as parameters.</UtilityInjection>

Mutating related item data before creating

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 can choose to return either the modified array of data, or null to prevent the item from being created:

use Filament\Forms\Components\Repeater;

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

        return $data;
    })

<UtilityInjection set="formFields" version="5.x" extras="Data;;array<string, mixed>;;$data;;The data that is being saved by the repeater.">You can inject various utilities into the function passed to mutateRelationshipDataBeforeCreateUsing() as parameters.</UtilityInjection>

Mutating related item data before saving

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 can choose to return either the modified array of data, or null to prevent the item from being saved:

use Filament\Forms\Components\Repeater;

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

        return $data;
    })

<UtilityInjection set="formFields" version="5.x" extras="Data;;array<string, mixed>;;$data;;The data that is being saved by the repeater.">You can inject various utilities into the function passed to mutateRelationshipDataBeforeSaveUsing() as parameters.</UtilityInjection>

Running code after creating a related item

You may run code after a new related item is created in the database using the afterCreate() method. This method accepts a closure that receives the current item's data in a $data variable and the newly created record in a $record variable. This is useful when you need the record's ID to perform additional operations, such as attaching pivot data:

use Filament\Forms\Components\Repeater;
use Illuminate\Database\Eloquent\Model;

Repeater::make('variants')
    ->relationship()
    ->schema([
        // ...
    ])
    ->afterCreate(function (array $data, Model $record): void {
        if (isset($data['attributes'])) {
            $record->attributes()->attach($data['attributes']);
        }
    })

<UtilityInjection set="formFields" version="5.x" extras="Data;;array<string, mixed>;;$data;;The data that was used to create the record.||Record;;Illuminate\Database\Eloquent\Model;;$record;;The newly created record.">You can inject various utilities into the function passed to afterCreate() as parameters.</UtilityInjection>

Running code after updating a related item

You may run code after an existing related item is updated in the database using the afterUpdate() method. This method accepts a closure that receives the current item's data in a $data variable and the updated record in a $record variable:

use Filament\Forms\Components\Repeater;
use Illuminate\Database\Eloquent\Model;

Repeater::make('variants')
    ->relationship()
    ->schema([
        // ...
    ])
    ->afterUpdate(function (array $data, Model $record): void {
        if (isset($data['attributes'])) {
            $record->attributes()->sync($data['attributes']);
        }
    })

<UtilityInjection set="formFields" version="5.x" extras="Data;;array<string, mixed>;;$data;;The data that was used to update the record.||Record;;Illuminate\Database\Eloquent\Model;;$record;;The updated record.">You can inject various utilities into the function passed to afterUpdate() as parameters.</UtilityInjection>

Running code after deleting a related item

You may run code after a related item is deleted from the database using the afterDelete() method. This method accepts a closure that receives the record that was just deleted in a $record variable. The record will no longer exist in the database at this point, but you can still access its attributes, such as its ID:

use Filament\Forms\Components\Repeater;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;

Repeater::make('attachments')
    ->relationship()
    ->schema([
        // ...
    ])
    ->afterDelete(function (Model $record): void {
        Storage::delete($record->file_path);
    })

<UtilityInjection set="formFields" version="5.x" extras="Record;;Illuminate\Database\Eloquent\Model;;$record;;The record that was just deleted.">You can inject various utilities into the function passed to afterDelete() as parameters.</UtilityInjection>

Modifying related records after retrieval

You may filter or modify the related records of a repeater after they are retrieved from the database using the modifyRecordsUsing argument. This method accepts a function that receives a Collection of related records. You should return the modified collection.

This can be particularly useful to restrict records to a specific group or category without doing this in the database query itself, which would trigger an extra query if the records are already eager loaded:

use Filament\Forms\Components\Repeater;
use Illuminate\Database\Eloquent\Collection;

Repeater::make('startItems')
    ->relationship(name: 'items', modifyRecordsUsing: fn (Collection $records): Collection => $records->where('group', 'start')),
Repeater::make('endItems')
    ->relationship(name: 'items', modifyRecordsUsing: fn (Collection $records): Collection => $records->where('group', 'end')),

<UtilityInjection set="formFields" version="5.x" extras="Records;;Illuminate\Database\Eloquent\Collection;;$records;;The collection of related records.">You can inject various utilities into the function passed to modifyRecordsUsing as parameters.</UtilityInjection>

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)

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.

<UtilityInjection set="formFields" version="5.x">As well as allowing a static value, the grid() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.</UtilityInjection>

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()
            ->live(onBlur: true),
        Select::make('role')
            ->options([
                'member' => 'Member',
                'administrator' => 'Administrator',
                'owner' => 'Owner',
            ])
            ->required(),
    ])
    ->columns(2)
    ->itemLabel(fn (array $state): ?string => $state[.....
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport
twbs/bootstrap4