Filament v3 to v4: Breaking Changes and Practical Refactor Patterns
#filament #laravel #upgrade #admin-panel

Filament v3 to v4: Breaking Changes and Practical Refactor Patterns

4 min read Mohamed Said Mohamed Said

Filament v3 → v4: Breaking Changes and Practical Refactor Patterns

Filament v4 is not a cosmetic release. The schema-based form and infolist API is a genuine architectural shift, and several method signatures changed in ways that will silently break behaviour if you only run composer update and call it done. This article walks through the highest-impact changes and shows concrete before/after code so you can plan your migration sprint.


1. The Unified Schema API

In v3, form() and infolist() each accepted a flat array of component objects. In v4 both methods receive a Schema instance, and components are registered through it.

v3

public static function form(Form $form): Form
{
    return $form->schema([
        TextInput::make('name')->required(),
        Select::make('status')->options(Status::class),
    ]);
}

v4

use Filament\Schemas\Schema;

public static function form(Schema $schema): Schema
{
    return $schema->components([
        TextInput::make('name')->required(),
        Select::make('status')->options(Status::class),
    ]);
}

The Form type hint is gone. If you type-hint Form in a v4 panel, the method simply will not be called — no exception, just a blank form. Run a project-wide search for : Form and : Infolist return types and replace them.


2. Action Class Signatures

Table and page actions in v3 accepted a Closure for most modifiers. v4 enforces typed builder callbacks in several places and drops a handful of magic $record-injected closures in favour of explicit fn (Model $record) signatures.

v3 — implicit injection

Action::make('approve')
    ->action(function ($record) {
        $record->approve();
    });

v4 — explicit type

Action::make('approve')
    ->action(function (Post $record): void {
        $record->approve();
    });

This is mostly additive, but if you relied on Filament resolving $livewire, $table, or $form from the closure signature, verify each one — some were removed from the resolver map.


3. Column and Field extraAttributes Merge Behaviour

v3 merged extra HTML attributes with a simple array_merge, meaning later calls won. v4 uses a keyed deep-merge. If you called extraAttributes() twice on the same component expecting the second call to win, you now get both merged. Audit any component that chains extraAttributes more than once.


4. Panel Provider Boot Order

v3 registered panel plugins inside register(). v4 moves plugin boot to a dedicated boot() hook on the plugin interface. If you wrote a custom plugin, implement boot(Panel $panel): void or your plugin's side effects (registering pages, widgets) will not fire.

class MyPlugin implements Plugin
{
    public function getId(): string { return 'my-plugin'; }

    public function register(Panel $panel): void
    {
        // bind services
    }

    public function boot(Panel $panel): void
    {
        // register pages, widgets, nav items
        $panel->pages([MyPage::class]);
    }
}

5. Infolist Entry Namespace Move

All infolist entry classes moved from Filament\Infolists\Components to Filament\Schemas\Components\Infolists. Your IDE will flag the missing imports, but if you have string-based component references in config files or dynamic resolution, those will fail silently at runtime.

# Quick grep to find stale imports
grep -r 'Filament\\Infolists\\Components' app/ --include='*.php'

Upgrade Checklist

  • Replace all : Form / : Infolist return types with : Schema
  • Change ->schema([]) calls on forms to ->components([])
  • Add explicit model types to action closures
  • Audit double extraAttributes() chains
  • Implement boot() on any custom plugins
  • Update infolist entry namespaces
  • Run php artisan filament:upgrade (the official codemod handles ~60% of the above)
  • Run your Pest feature suite against a staging panel before deploying

Key Takeaways

  • The Schema API is the single biggest surface-area change — fix type hints first.
  • Silent failures (blank forms, missing nav items) are more common than exceptions; test visually.
  • The official upgrade command is a starting point, not a finish line.
  • Custom plugins need a boot() method or they are effectively broken in v4.
  • Namespace moves are grep-able; automate the find-and-replace before manual review.

Found this useful?

Frequently Asked Questions

3 questions
Q01 Can I run Filament v3 and v4 panels side by side during migration?
No. Filament v4 is a single Composer package version. You cannot mix v3 and v4 panel providers in the same application. Migrate all panels at once, or maintain a separate branch for the upgrade.
Q02 Does `php artisan filament:upgrade` handle all the breaking changes automatically?
It handles the most mechanical changes like form/infolist return type replacements and some namespace moves, but it cannot safely rewrite action closure signatures or plugin boot logic. Always review its diff and run your full test suite afterwards.
Q03 Are third-party Filament plugins compatible with v4 out of the box?
Only if the plugin author has released a v4-compatible version. Check the plugin's GitHub releases and composer.json constraints before upgrading. Many popular plugins had v4 releases within weeks of the stable launch, but niche ones may lag.

Continue reading

More Articles

View all