Managing Complex Nested Relationships in FilamentPHP: Beyond the Repeater
Stop fighting deeply nested Repeaters in FilamentPHP. Learn how to use separate Resources and Relation Managers to handle complex Eloquent relationships while maintaining performance and strict authorization.
14 Mar 2026, 16:51 UTC

The Problem: The Monolithic Form Wall
When building an admin panel with Filament, it is tempting to handle all related data within a single resource using the Repeater component. For a simple Order with a few Items, this works perfectly. However, as the data model grows—for example, a Product that has many Variants, and each variant has many Images—the single-resource approach fails.
The useful takeaway: To maintain performance and maintainability in Filament v3, replace deeply nested repeaters with a combination of separate Resources and Relation Managers. This shifts the complexity from a single massive form to isolated, model-driven components.
Why Nested Repeaters Scale Poorly
Filament resources are powered by Livewire. When you nest a Repeater inside another Repeater, you create several technical bottlenecks:
- State Hydration: Every change in a nested field requires Livewire to synchronize the entire state tree. Deep nesting increases the payload size and can lead to noticeable lag.
- Validation Collision: Writing validation rules for a child of a child (e.g., ensuring a Variant Image is unique to that specific Variant) becomes verbose and difficult to manage within a single
form()method. - Authorization Gaps: Laravel Policies are typically bound to a model. If all editing happens in the
ProductResource, you lose the ability to use aVariantPolicyto restrict who can change pricing versus who can change images.
The Engineering Pattern: Resource + Relation Manager
Instead of one monolithic form, treat each major entity as its own Resource and link them using Relation Managers. This allows each entity to have its own dedicated list view, create/edit modals, and policy-based access control.
1. Generate Independent Resources
Run the following commands in your terminal to create the necessary scaffolding. Use the --generate flag to let Filament attempt to guess the fields from your database schema:
php artisan make:filament-resource Product --generate
php artisan make:filament-resource Variant --generate
php artisan make:filament-resource VariantImage --generate
2. Implement the Relation Manager
Create a Relation Manager for the Variant model to be used inside the ProductResource. This allows you to manage variants in a table format directly on the Product edit page.
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Forms;
use Filament\Tables;
class VariantsRelationManager extends RelationManager
{
protected static string $relationship = 'variants';
public function form(Forms\Form $form): Forms\Form
{
return $form->schema([
Forms\Components\TextInput::make('sku')->required(),
Forms\Components\TextInput::make('price')->numeric()->prefix('$'),
Forms\Components\Select::make('size_id')
->relationship('size', 'name')
->searchable(),
]);
}
public function table(Tables\Table $table): Tables\Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('sku')->searchable(),
Tables\Columns\TextColumn::make('price')->money('USD'),
])
->headerActions([Tables\Actions\CreateAction::make()])
->actions([Tables\Actions\EditAction::make(), Tables\Actions\DeleteAction::make()]);
}
}
To activate this, add the manager to the getRelations() method in your ProductResource class.
Worked Example: Conditional Visibility in Relation Managers
A common requirement is to show fields only when certain conditions are met. Because Relation Managers use the same form builder as Resources, you can use the visible() or hidden() methods with the Get utility to create reactive forms without writing custom JavaScript.
Forms\Components\TextInput::make('special_instruction')
->visible(fn (Forms\Get $get) => $get('is_custom_order') === true)
This logic is handled by Alpine.js on the frontend, providing an instant user experience while keeping the logic defined in PHP.
Trade-offs and Limitations
- Click Depth: Users must navigate from Product → Variant → Image. This is more clicks than a single page. To mitigate this, use
->modal()actions in your tables to allow editing without leaving the current page. - Eager Loading: If you display related data in the Relation Manager table, ensure you use
->with(['relationship'])in your query to avoid N+1 query issues. - UI Density: Having too many Relation Managers on one page can clutter the interface. Use Filament's Tabbed layout or separate custom pages for extremely complex aggregates.
Verification and Testing
To verify this implementation, perform the following checks:
- Permissions: Create a
VariantPolicy. Ensure that a user without 'update' permissions for Variants cannot see the Edit action in theVariantsRelationManager, even if they can edit the Product. - Data Integrity: Create a Variant via the Relation Manager and verify the
product_idis automatically assigned by Filament. - Performance: Open the browser's Network tab. Compare the payload size of a page with a deeply nested Repeater versus a page using a Relation Manager; the latter should be significantly smaller upon initial load.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.