Filament Repeater: Set Values Manually or From Other Fields

If you use a Filament Repeater field, you may need to set its values manually: upfront on the page load or based on changes in other fields. In this tutorial, we will show you three scenarios to achieve it.

Imagine a scenario: you have a checkbox for Multi-Language, and ticking it adds three rows to the Repeater for three default languages.

How to implement it?


Preparation: DB/Model Structure

Repeater values should be saved as a JSON column in the database. Additionally, the column should be cast to an array. In this tutorial, the Post Model will be used.

database/migrations/xxx_create_posts_table.php:

public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->json('title_translations')->nullable();
$table->timestamps();
});
}

app/Models/Post.php:

class Post extends Model
{
protected $fillable = [
'title',
'title_translations',
];
 
protected function casts(): array
{
return [
'title_translations' => 'array',
];
}
}

In the Filament Resource, the initial form code (without the multi-language checkbox, for now) is below:

class PostResource extends Resource
{
// ...
 
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('title')
->columnSpanFull(),
Forms\Components\Repeater::make('title_translations')
->hidden(fn (Forms\Get $get) => ! $get('multi_language'))
->columnSpanFull()
->maxItems(3)
->addable(false)
->reorderableWithDragAndDrop(false)
->grid(3)
->schema([
Forms\Components\Select::make('language')
->options([
'en' => 'English',
'es' => 'Spanish',
'de' => 'German',
])
->disableOptionsWhenSelectedInSiblingRepeaterItems(),
Forms\Components\TextInput::make('title'),
])
]);
}
 
// ...
}

Now, how do we add three repeater items by default, one for each language?


Option 1: Default Values on Page Loading

Did you know that you can set repeater ->default() values as an array?

class PostResource extends Resource
{
// ...
 
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('title')
->columnSpanFull(),
Forms\Components\Repeater::make('title_translations')
->columnSpanFull()
->maxItems(3)
->addable(false)
->reorderableWithDragAndDrop(false)
->grid(3)
->default([
['language' => 'en', 'title' => ''],
['language' => 'es', 'title' => ''],
['language' => 'de', 'title' => ''],
])
->schema([
Forms\Components\Select::make('language')
->options([
'en' => 'English',
'es' => 'Spanish',
'de' => 'German',
])
->disableOptionsWhenSelectedInSiblingRepeaterItems(),
Forms\Components\TextInput::make('title'),
])
]);
}
 
// ...
}

This way, three repeater items will be added when you visit the Create page.


Option 2: Set Values after Condition

Now, we get back to the initial example mentioned in the beginning. We will have a checkbox that, after ticking, will show a Repeater with three items.

First, let's add the checkbox which will conditionally show/hide the Repeater.

class PostResource extends Resource
{
// ...
 
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('title')
->columnSpanFull(),
Forms\Components\Checkbox::make('multi_language')
->live()
->columnSpanFull()
->dehydrated(false)
->formatStateUsing(fn (?Post $record) => (bool) $record?->title_translations)
->disabled(fn (?Post $record, $context) => (bool) $record?->title_translations && $context == 'edit'),
Forms\Components\Repeater::make('title_translations')
->hidden(fn (Forms\Get $get) => ! $get('multi_language'))
->columnSpanFull()
->maxItems(3)
->addable(false)
->reorderableWithDragAndDrop(false)
->grid(3)
->schema([
Forms\Components\Select::make('language')
->options([
'en' => 'English',
'es' => 'Spanish',
'de' => 'German',
])
->disableOptionsWhenSelectedInSiblingRepeaterItems(),
Forms\Components\TextInput::make('title'),
])
]);
}
 
// ...
}

Now, we need to set values for the Repeater after the checkbox is checked or after the state is updated.

We will use the afterStateUpdated() method, which will accept $set and $get as a callable function.

Before setting the Repeater values, we must check the count of Repeater items. We only need to do something if there are fewer than three items.

When setting the values with $set([name], [values]), there are two parameters:

  • The first parameter is the Repeater or DB column name
  • The second parameter is the array with the values
class PostResource extends Resource
{
// ...
 
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('title')
->columnSpanFull(),
Forms\Components\Checkbox::make('multi_language')
->live()
->columnSpanFull()
->dehydrated(false)
->formatStateUsing(fn (?Post $record) => (bool) $record?->title_translations)
->disabled(fn (?Post $record, $context) => (bool) $record?->title_translations && $context == 'edit'),
->afterStateUpdated(function (Forms\Set $set, Forms\Get $get) {
if (count($get('title_translations')) == 3) {
return;
}
 
$set('title_translations', [
['language' => 'en', 'title' => ''],
['language' => 'es', 'title' => ''],
['language' => 'de', 'title' => ''],
]);
}),
Forms\Components\Repeater::make('title_translations')
->hidden(fn (Forms\Get $get) => ! $get('multi_language'))
->columnSpanFull()
->maxItems(3)
->addable(false)
->reorderableWithDragAndDrop(false)
->grid(3)
->schema([
Forms\Components\Select::make('language')
->options([
'en' => 'English',
'es' => 'Spanish',
'de' => 'German',
])
->disableOptionsWhenSelectedInSiblingRepeaterItems(),
Forms\Components\TextInput::make('title'),
])
]);
}
 
// ...
}

After checking the checkbox, we see three Repeater items with different languages selected.


Option 3: Livewire Component: No Filament Resource

The last scenario: same but outside Filament Resource.

If you use Filament forms outside of the panel or a custom Filament page, you might also need to set some initial Repeater values.

Those values could be hard-coded or come from the database.

One way is to set default options on the Repeater itself. Another way is to set them when initializing the form with $this->form->fill() in mount().

class Post extends Page implements HasForms
{
use InteractsWithForms;
 
public ?array $data = [];
 
public function mount(): void
{
$this->form->fill([
'title_translations' => [
['language' => 'en', 'title' => ''],
['language' => 'es', 'title' => ''],
['language' => 'de', 'title' => ''],
],
]);
}
 
public function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('title')
->columnSpanFull(),
Forms\Components\Repeater::make('title_translations')
->columnSpanFull()
->maxItems(3)
->addable(false)
->reorderableWithDragAndDrop(false)
->grid(3)
->schema([
Forms\Components\Select::make('language')
->options([
'en' => 'English',
'es' => 'Spanish',
'de' => 'German',
])
->disableOptionsWhenSelectedInSiblingRepeaterItems(),
Forms\Components\TextInput::make('title'),
])
])
->statePath('data');
}
}

If you want more Filament examples, you can find more real-life projects on our FilamentExamples.com.

No comments or questions yet...

Like our articles?

Become a Premium Member for $129/year or $29/month
What else you will get:
  • 59 courses (1057 lessons, total 42 h 44 min)
  • 79 long-form tutorials (one new every week)
  • access to project repositories
  • access to private Discord

Recent Premium Tutorials