
Brings the full power of Spatie Laravel Model States to Filament with zero complexity and beautiful visual workflows.
Filament StateFusion is a powerful FilamentPHP plugin that seamlessly integrates Spatie Laravel Model States into the Filament admin panel. Effortlessly manage model states, transitions, and filtering within Filament, enhancing both user experience and developer productivity.
Perfect for order processing, publishing workflows, approval systems, and any application requiring well-defined state management.
✨ Rich State Management
🛠Developer Experience
🎨 Customizable Interface
📸 Screenshots and demo GIFs will be added soon
This plugin is designed to work with the following dependencies:
Install the package via Composer:
| Plugin Version | Filament Version | Readme |
|---|---|---|
| 1.x | 3.x | Link |
| 2.x | 4.x | Link |
composer require a909m/filament-statefusion
composer require a909m/filament-statefusion:2.0
StateFusionBulkAction and StateFusionAction for pages and tablesThen, implement the HasFilamentStateFusion interface and use the StateFusionInfo trait on your abstract state class.
Finally, you can start using the components and actions provided by this plugin in your Filament resources.
This plugin builds on Spatie Laravel Model States, which provides:
Pending → Processing)Example: An order can be Pending, Processing, Shipped, or Cancelled. You define:
Pending can become Processing, but Shipped cannot become Pending)StateFusion makes these states visual and interactive in Filament with dropdowns, badges, filters, and action buttons.
First, ensure you have Spatie Laravel Model States configured. Then implement the HasFilamentStateFusion interface and use the StateFusionInfo trait on your abstract state class:
<?php// app/States/OrderState.phpuse A909M\FilamentStateFusion\Concerns\StateFusionInfo;use A909M\FilamentStateFusion\Contracts\HasFilamentStateFusion;use Spatie\ModelStates\State;use Spatie\ModelStates\StateConfig;Â abstract class OrderState extends State implements HasFilamentStateFusion{ use StateFusionInfo;Â public static function config(): StateConfig { return parent::config() ->default(PendingState::class) ->allowTransition(PendingState::class, ProcessingState::class) ->allowTransition(ProcessingState::class, ShippedState::class) ->allowTransition(ShippedState::class, DeliveredState::class) ->allowTransition([PendingState::class, ProcessingState::class], CancelledState::class); }}
New to Spatie Laravel Model States? Read their introduction first to understand states, transitions, and the state pattern.
Add the state to your Eloquent model:
<?php// app/Models/Order.php use App\Models\States\OrderState;use Illuminate\Database\Eloquent\Model;use App\States\OrderState; class Order extends Model{ use HasStates; protected $casts = [ 'status' => OrderState::class, ]; }
Now you can use StateFusion components in your Filament resources:
// app/Filament/Resources/OrderResource.phpuse A909M\FilamentStateFusion\Tables\Columns\StateFusionSelectColumn;use A909M\FilamentStateFusion\Tables\Filters\StateFusionSelectFilter;use Filament\Forms\Form;use Filament\Resources\Resource;use Filament\Tables\Table; class OrderResource extends Resource{ // ... public static function table(Table $table): Table { return $table ->columns([ StateFusionSelectColumn::make('status'), ]) ->filters([ StateFusionSelectFilter::make('status'), ]); }}
You can use the following state-aware components in your forms:
StateFusionSelect
StateFusionRadio
StateFusionToggleButtons
use A909M\FilamentStateFusion\Forms\Components\StateFusionSelect;use A909M\FilamentStateFusion\Forms\Components\StateFusionRadio;use A909M\FilamentStateFusion\Forms\Components\StateFusionToggleButtons; // Dropdown selectStateFusionSelect::make('status'), // Radio buttons with descriptionsStateFusionRadio::make('status'), // Toggle buttons with colors and iconsStateFusionToggleButtons::make('status'),
Display state information in your Filament tables and allow for quick state transitions.
use A909M\FilamentStateFusion\Tables\Columns\StateFusionSelectColumn;Â StateFusionSelectColumn::make('status'),
You can also use the standard TextColumn to display the state as a badge. When your state classes implement HasLabel, HasColor, and HasIcon interfaces, the badge will automatically display the appropriate label, color, and icon without any additional configuration.
use Filament\Tables\Columns\TextColumn;Â TextColumn::make('status') ->badge(),
Filter records by state:
use A909M\FilamentStateFusion\Tables\Filters\StateFusionSelectFilter;Â StateFusionSelectFilter::make('status'),
Filter records using tabs on the listing page (see Filament Tabs Documentation):
use A909M\FilamentStateFusion\StateFusionTabs;Â public function getTabs(): array{ return StateFusionTabs::make($this->getModel())->toArray();}
Options:
// With "All" tab and custom attributeStateFusionTabs::make($this->getModel()) ->includeAll() ->badge(false) // Disabled the badge ->attribute('status') // Use a specific state attribute (if your model has multiple state attributes) ->toArray()
Similar to the table column, you can use the standard TextEntry in your infolists to display the model state as a badge. When your state classes implement HasLabel, HasColor, and HasIcon interfaces, the badge will automatically display the appropriate label, color, and icon without any additional configuration.
use Filament\Infolists\Components\TextEntry;Â TextEntry::make('status') ->badge(),
Single record state transitions:
use A909M\FilamentStateFusion\Actions\StateFusionAction;use App\Models\States\ProcessingState;Â Â StateFusionAction::make('approve') ->transitionTo(ProcessingState::class),
Bulk state transitions with validation:
use A909M\FilamentStateFusion\Actions\StateFusionBulkAction;Â StateFusionBulkAction::make('bulkProcess') ->transition(PendingState::class, ProcessingState::class),
Generate all possible transitions automatically:
use A909M\FilamentStateFusion\Actions\StateFusionActionGroup;Â StateFusionActionGroup::generate('status', OrderState::class)
By default, the state components use the first attribute defined in your model's getDefaultStates() as the state attribute. If your model uses a different attribute name or you want to specify which attribute to use, you can set it using the attribute() method.
// Using a different attribute nameStateFusionSelect::make('approval_status') ->attribute('approval_status'), // Dynamic attribute selectionStateFusionAction::make('transition') ->attribute('state') ->transitionTo(ApprovedState::class),
This is useful when your model has multiple state attributes or you want to reuse the component for different attributes.
To customize how states appear in the UI, implement the HasLabel, HasDescription, HasColor, or HasIcon interfaces on your concrete state classes:
use Filament\Support\Colors\Color;use Filament\Support\Contracts\HasColor;use Filament\Support\Contracts\HasDescription;use Filament\Support\Contracts\HasIcon;use Filament\Support\Contracts\HasLabel;Â final class CancelledState extends OrderState implements HasDescription, HasColor, HasIcon, HasLabel{ public function getLabel(): string { return __('Order Cancelled'); }Â public function getColor(): array { return Color::Red; }Â public function getIcon(): string { return 'heroicon-o-x-circle'; }Â public function getDescription(): ?string { return 'Order cancelled, transaction reversed.'; }}
Similarly, transitions can be customized by implementing the same interfaces:
use Filament\Support\Colors\Color;use Filament\Support\Contracts\HasColor;use Filament\Support\Contracts\HasIcon;use Filament\Support\Contracts\HasLabel;use Spatie\ModelStates\Transition;Â final class ToCancelled extends Transition implements HasLabel, HasColor, HasIcon{ public function getLabel(): string { return __('Mark as Cancelled'); }Â public function getColor(): array { return Color::Red; }Â public function getIcon(): string { return 'heroicon-o-x-circle'; }}
Sometimes you need to collect additional information when transitioning between states. StateFusion makes this easy by letting you add a form() method to your custom transition classes.
Example: Cancelling an order with a reason
<?php use Filament\Forms\Components\Textarea;use Filament\Support\Colors\Color;use Filament\Support\Contracts\{HasLabel, HasColor, HasIcon};use Spatie\ModelStates\Transition; final class CancelOrder extends Transition implements HasLabel, HasColor, HasIcon{ public function __construct( private Order $order, private ?array $data = null ) {} public function handle(): Order { $this->order->state = new CancelledState($this->order); $this->order->cancellation_reason = $this->data['reason']; $this->order->cancelled_at = now(); $this->order->save(); return $this->order; } public function form(): array { return [ Textarea::make('reason') ->label('Cancellation Reason') ->placeholder('Why are you cancelling this order?') ->required() ->maxLength(500), ]; } public function getLabel(): string { return 'Cancel Order'; } public function getColor(): string | array { return Color::Red; } public function getIcon(): string { return 'heroicon-o-x-circle'; }}
The form data is automatically passed to your transition's $data parameter, which you can then use in the handle() method to update your model.
Learn more: Custom Transition Classes in the Spatie documentation.
composer test
Please see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details on how to contribute to this project.
Please review our security policy on how to report security vulnerabilities.
The MIT License (MIT). Please see License File for more information.