Files
filament/docs/14-upgrade-guide.md
2026-07-17 10:44:39 +01:00

15 KiB
Raw Permalink Blame History

title
title
Upgrade guide

import Aside from "@components/Aside.astro"

Running the automated upgrade script

The first step to upgrade your Filament app is to run the automated upgrade script. This script will automatically upgrade your application to the latest version of Filament and make changes to your code, which handles breaking changes:

composer require filament/upgrade:"^6.0" -W --dev

vendor/bin/filament-v6

# Run the commands output by the upgrade script, they are unique to your app
composer require filament/filament:"^6.0" -W --no-update
composer update

Make sure to carefully follow the instructions, and review the changes made by the script. You may need to make some manual changes to your code afterwards, but the script should handle most of the repetitive work for you.

You can now composer remove filament/upgrade --dev as you don't need it anymore.

Breaking changes that must be handled manually

To begin, filter the upgrade guide for your specific needs by selecting only the packages that you use in your project:

Panels
<Checkbox value="forms" model="packages">
    Forms

    <span slot="description">
        This package is also often used in a panel, or using the tables or actions package.
    </span>
</Checkbox>

<Checkbox value="infolists" model="packages">
    Infolists

    <span slot="description">
        This package is also often used in a panel, or using the tables or actions package.
    </span>
</Checkbox>

<Checkbox value="tables" model="packages">
    Tables

    <span slot="description">
        This package is also often used in a panel.
    </span>
</Checkbox>

<Checkbox value="actions" model="packages">
    Actions

    <span slot="description">
        This package is also often used in a panel.
    </span>
</Checkbox>

<Checkbox value="notifications" model="packages">
    Notifications

    <span slot="description">
        This package is also often used in a panel.
    </span>
</Checkbox>

<Checkbox value="widgets" model="packages">
    Widgets

    <span slot="description">
        This package is also often used in a panel.
    </span>
</Checkbox>

<Checkbox value="support" model="packages">
    Blade UI components
</Checkbox>

Medium-impact changes

`FileUpload` now prevents file path tampering by default

To protect applications whose storage disks hold files belonging to more than one user, tenant, or record, the FileUpload field now validates submitted file paths by default. Previously this check was opt-in via preventFilePathTampering().

Every submitted string path is now compared against the value originally loaded from the record (via $record->getOriginal() for the attribute matching the field name). Paths that do not match cause the field to fail validation, so the record is never saved with a tampered value. Newly uploaded files, cleared values, and — for multiple() fields — each individual entry are all handled as before.

This only affects fields that submit a string path that was not already stored on the record. Most applications never do this, so no changes are required. However, if you rely on a flow that sets the field to a path outside the record — for example, an action that selects a pre-uploaded template file, a "copy from another record" button, or a field seeded with a preset path via ->default('path/to/file.png') on a create form — those submissions will now fail validation.

You have three options, in order of preference:

  • Approve the specific paths using the allowFilePathUsing argument, so only known-safe paths are permitted:
use Filament\Forms\Components\FileUpload;

FileUpload::make('avatar')
    ->preventFilePathTampering(
        allowFilePathUsing: fn (string $file): bool => str_starts_with($file, 'templates/'),
    )
  • Disable the protection on the individual fields that need it:
use Filament\Forms\Components\FileUpload;

FileUpload::make('avatar')
    ->preventFilePathTampering(false)
  • Restore the previous behavior for every FileUpload in your application by calling configureUsing() in a service provider's boot() method. This reintroduces the tampering risk across your whole app, so prefer one of the options above:
use Filament\Forms\Components\FileUpload;

FileUpload::configureUsing(function (FileUpload $component): void {
    $component->preventFilePathTampering(false);
});

See the file upload documentation for more details.

`RichEditor` now prevents file attachment path tampering by default

For the same reason as the FileUpload change above, the RichEditor field now validates the data-id attributes of image nodes in submitted content by default. Previously this check was opt-in via preventFileAttachmentPathTampering().

Filament parses the record's original content (via $record->getOriginal() for the attribute matching the field name) and allows only the data-id values already present. Any other existing data-id causes the field to fail validation, so the record is never saved with a tampered value. Newly uploaded images always pass through. If you use the spatie/laravel-medialibrary plugin as the file attachment provider, this protection was already implicit and its behavior is unchanged.

This only affects editors that insert a data-id that was not already stored on the record — for example, an action that inserts an image from a shared library, or a "copy from another record" button. If you rely on such a flow, you have the same three options as for FileUpload:

  • Approve the specific identifiers using the allowFilePathUsing argument:
use Filament\Forms\Components\RichEditor;

RichEditor::make('content')
    ->preventFileAttachmentPathTampering(
        allowFilePathUsing: fn (string $file): bool => str_starts_with($file, 'templates/'),
    )
  • Disable the protection on the individual fields that need it:
use Filament\Forms\Components\RichEditor;

RichEditor::make('content')
    ->preventFileAttachmentPathTampering(false)
  • Restore the previous behavior for every RichEditor in your application by calling configureUsing() in a service provider's boot() method. This reintroduces the tampering risk across your whole app, so prefer one of the options above:
use Filament\Forms\Components\RichEditor;

RichEditor::configureUsing(function (RichEditor $component): void {
    $component->preventFileAttachmentPathTampering(false);
});

See the rich editor documentation for more details.

Exports now prevent CSV/XLSX formula injection by default

To protect people who open exported files in spreadsheet software, every ExportColumn now neutralizes CSV/XLSX formula injection by default. Previously this check was opt-in via preventFormulaInjection().

Any exported string value that begins with a formula-triggering character (=, +, -, @, a tab, or a carriage return) is now prefixed with a single quote (') so that spreadsheet software treats it as plain text. Purely numeric strings such as -5 are left unchanged, since spreadsheets interpret them as numbers rather than formulas.

This only changes the output for string values that begin with one of those characters and are not purely numeric — for example, a phone number stored as a string like +44 1234 567890 will now be exported as '+44 1234 567890. If a specific column exports trusted data where this transformation is unwanted, disable the protection for that column:

use Filament\Actions\Exports\ExportColumn;

ExportColumn::make('phone')
    ->preventFormulaInjection(false)

To restore the previous behavior for every export column in your application, call configureUsing() in a service provider's boot() method. This reintroduces the formula injection risk across all your exports, so prefer disabling it only on the individual columns that need it:

use Filament\Actions\Exports\ExportColumn;

ExportColumn::configureUsing(function (ExportColumn $column): void {
    $column->preventFormulaInjection(false);
});

See the export documentation for more details.

Import failure CSVs now prevent formula injection by default

When rows fail validation during an import, Filament compiles them into a downloadable failure CSV. To protect people who open that file in spreadsheet software, formula injection is now neutralized in the failure CSV by default. Previously this check was opt-in via preventFormulaInjection().

Any cell that begins with a formula-triggering character (=, +, -, @, a tab, or a carriage return) is now prefixed with a single quote ('). Purely numeric strings such as -5 are left unchanged, so the failure CSV can still be corrected and re-uploaded without corrupting legitimate data.

This only changes the failure CSV output for values that begin with one of those characters and are not purely numeric — for example, a phone number stored as a string like +44 1234 567890 will now appear as '+44 1234 567890. If a specific importer processes trusted files where this transformation is unwanted, disable it by redeclaring the property on your importer class:

use Filament\Actions\Imports\Importer;

class ProductImporter extends Importer
{
    protected static bool $shouldPreventFormulaInjection = false;
}

To restore the previous behavior for every importer in your application, call preventFormulaInjection(false) in a service provider's boot() method:

use Filament\Actions\Imports\Importer;

Importer::preventFormulaInjection(false);

See the import documentation for more details.

Livewire file uploads are now restricted to schema components by default

Every Livewire component that uses the InteractsWithSchemas trait exposes Livewire's _startUpload and _finishUpload RPC methods, which by default accept uploads to any property name — even ones that are not real upload fields. To close this, Filament now restricts these uploads by default: _startUpload and _finishUpload abort with a 403 unless the target property maps to a FileUpload field (or any field that supports file attachments) registered in one of the component's schemas. Previously this was opt-in via the RestrictsFileUploadsToSchemaComponents trait.

Legitimate uploads from your schema's fields are unaffected. This only changes behavior for components that accept uploads to a property that is not a schema field — for example, a custom Livewire component that wires wire:model for a file upload to a property outside its Filament schema.

The RestrictsFileUploadsToSchemaComponents trait has been removed, since its behavior is now the default. The upgrade command removes any usage of it from your components automatically. If a component legitimately needs to accept uploads to a non-schema property, opt out by overriding the method:

public function shouldRestrictFileUploadsToSchemaComponents(): bool
{
    return false;
}

See the security documentation for more details.

Low-impact changes

The modal content is now wrapped in a `fi-modal-window-scroll` element when using a sticky header or footer

To fix an issue where modals with a sticky header or footer could overflow the viewport, the internal DOM structure of the modal component has changed. When a modal has a sticky header (stickyHeader()) or a sticky footer (stickyFooter()), and is not a slide-over or a screen-width modal, its heading, content, and footer are now wrapped in a new scrollable fi-modal-window-scroll element:

<div class="fi-modal-window">
    <div class="fi-modal-window-scroll">
        <div class="fi-modal-header"><!-- ... --></div>
        <div class="fi-modal-content"><!-- ... --></div>
        <div class="fi-modal-footer"><!-- ... --></div>
    </div>
</div>

If you have written custom CSS or a theme that targets fi-modal-header, fi-modal-content, or fi-modal-footer as direct children of fi-modal-window, you should update your selectors to account for the new fi-modal-window-scroll wrapper.

The `ryangjchandler/blade-capture-directive` Composer dependency has been removed

Filament no longer depends on the ryangjchandler/blade-capture-directive package, since Filament registers its own @capture and @endcapture Blade directives with the same functionality. If you use these directives in your own Blade views, they will continue to work without any changes.

If you use @capture outside of Filament and want to ensure that the directives remain available even if you remove Filament from your project, you can install the package yourself:

composer require ryangjchandler/blade-capture-directive

If you are a plugin developer and you register the package's service provider in your test cases, for example in the getPackageProviders() method of a Testbench test case, you should remove RyanChandler\BladeCaptureDirective\BladeCaptureDirectiveServiceProvider from the list.