A collection of essential traits that streamline Filament plugin development by taking care of the boilerplate, so you can focus on shipping real features faster
- Filament Plugin Essentials
- Easily Configure
- ** Navigation** - Complete control over resource navigation (labels, icons, groups, sorting, badges)
- ️ Label - Model labels, plural forms, title attributes, and casing options
- ** Global Search** - Searchability controls, result limits, and case sensitivity options
- ** Resource Tenant options** - Tenant scoping and relationship configuration
- ** Parent Resource** - Hierarchical resource relationships
- ️ Multi-Resource Configuration - Different settings per
Resourcein a single plugin - ** 3-Tier Default System** - User overrides → Plugin defaults → Filament defaults
- ** Dynamic Values** - Closure support for conditional logic and real-time data
- ** Developer-Friendly** - Minimal boilerplate with maximum customization
composer require bezhansalleh/filament-plugin-essentials<?php
namespace YourVendor\YourPlugin;
use Webkernel\Component\Plugin\Concerns\Plugin;
use Filament\Contracts\Plugin;
class YourPlugin implements Plugin
{
use Plugin\HasNavigation;
use Plugin\HasLabels;
use Plugin\HasGlobalSearch;
use Plugin\WithMultipleResourceSupport; // For multi-forResource plugins
public static function make(): static
{
return app(static::class);
}
public function getId(): string
{
return 'your-plugin';
}
// ... rest of plugin implementation
}<?php
namespace YourVendor\YourPlugin\Resources;
use Webkernel\Component\Plugin\Concerns;
use Filament\Resources\Resource;
class UserResource extends Resource
{
use Concerns\Resource\HasNavigation;
use Concerns\Resource\HasLabels;
use Concerns\Resource\HasGlobalSearch;
protected static ?string $model = User::class;
// Required: Link resource to plugin
public static function getEssentialsPlugin(): ?YourPlugin
{
return YourPlugin::get();
}
// ... rest of forResource implementation
}class YourPlugin implements Plugin
{
use HasNavigation, HasLabels, HasGlobalSearch;
protected function getPluginDefaults(): array
{
return [
// Global defaults (apply to all resources)
'navigationGroup' => 'Your Plugin',
'navigationIcon' => 'heroicon-o-puzzle-piece',
'modelLabel' => 'Item',
'pluralModelLabel' => 'Items',
'globalSearchResultsLimit' => 25,
// Resource-specific defaults (optional)
'resources' => [
UserResource::class => [
'modelLabel' => 'User',
'pluralModelLabel' => 'Users',
'navigationIcon' => 'heroicon-o-users',
'globalSearchResultsLimit' => 50,
],
PostResource::class => [
'modelLabel' => 'Post',
'pluralModelLabel' => 'Posts',
'navigationIcon' => 'heroicon-o-document-text',
'navigationSort' => 10,
],
],
];
}
}When plugin developers use these traits, users of their plugins get a fluent API to configure them. The available configuration options depend on which traits the plugin developer chose to include.
Configure any plugin that uses these traits:
use YourVendor\YourPlugin\YourPlugin;
public function panel(Panel $panel): Panel
{
return $panel
->plugins([
YourPlugin::make()
->navigationLabel('Custom Label')
->navigationIcon('heroicon-o-star')
->modelLabel('Custom Item')
->globalSearchResultsLimit(30),
]);
}YourPlugin::make()
// Configure UserResource
->forResource(UserResource::class)
->navigationLabel('Users')
->modelLabel('User')
->globalSearchResultsLimit(25)
// Configure PostResource
->forResource(PostResource::class)
->navigationLabel('Posts')
->modelLabel('Article')
->globalSearchResultsLimit(10)YourPlugin::make()
->navigationLabel(fn() => 'Users (' . User::count() . ')')
->navigationBadge(fn() => User::whereNull('email_verified_at')->count())
->modelLabel(fn() => auth()->user()->isAdmin() ? 'Admin User' : 'User')Each plugin trait has a corresponding forResource trait that must be added to your forResource classes:
use Webkernel\Component\Plugin\Concerns\Plugin; // plugin
use Webkernel\Component\Plugin\Concerns\Resource; // forResource| Plugin Trait | Resource Trait |
|---|---|
Plugin\HasNavigation |
Resource\HasNavigation |
Plugin\HasLabels |
Resource\HasLabels |
Plugin\HasGlobalSearch |
Resource\HasGlobalSearch |
Plugin\BelongsToParent |
Resource\BelongsToParent |
Plugin\BelongsToTenant |
Resource\BelongsToTenant |
Plugin\WithMultipleResourceSupport |
(No forResource trait needed - enables multi-forResource configuration) |
$plugin
->navigationLabel('Label') // string|Closure|null
->navigationIcon('heroicon-o-home') // string|Closure|null
->activeNavigationIcon('heroicon-s-home') // string|Closure|null
->navigationGroup('Group') // string|Closure|null
->navigationSort(10) // int|Closure|null
->navigationBadge('5') // string|Closure|null
->navigationBadgeColor('success') // string|array|Closure|null
->navigationParentItem('parent.item') // string|Closure|null
->slug('custom-slug') // string|Closure|null
->registerNavigation(false); // bool|ClosureCopy-paste defaults:
protected function getPluginDefaults(): array
{
return [
'navigationLabel' => 'Your Label',
'navigationIcon' => 'heroicon-o-home',
'activeNavigationIcon' => 'heroicon-s-home',
'navigationGroup' => 'Your Group',
'navigationSort' => 10,
'navigationBadge' => null,
'navigationBadgeColor' => null,
'navigationParentItem' => null,
'slug' => null,
'registerNavigation' => true,
];
}$plugin
->modelLabel('Model') // string|Closure|null
->pluralModelLabel('Models') // string|Closure|null
->recordTitleAttribute('name') // string|Closure|null
->titleCaseModelLabel(false); // bool|ClosureCopy-paste defaults:
protected function getPluginDefaults(): array
{
return [
'modelLabel' => 'Item',
'pluralModelLabel' => 'Items',
'recordTitleAttribute' => 'name',
'titleCaseModelLabel' => true,
];
}$plugin
->globallySearchable(true) // bool|Closure
->globalSearchResultsLimit(50) // int|Closure
->forceGlobalSearchCaseInsensitive(true) // bool|Closure|null
->splitGlobalSearchTerms(false); // bool|ClosureCopy-paste defaults:
protected function getPluginDefaults(): array
{
return [
'globallySearchable' => true,
'globalSearchResultsLimit' => 50,
'forceGlobalSearchCaseInsensitive' => null,
'splitGlobalSearchTerms' => false,
];
}$plugin->parentResource(ParentResource::class); // string|Closure|nullCopy-paste defaults:
protected function getPluginDefaults(): array
{
return [
'parentResource' => null,
];
}$plugin
->scopeToTenant(true) // bool|Closure
->tenantRelationshipName('organization') // string|Closure|null
->tenantOwnershipRelationshipName('owner'); // string|Closure|nullCopy-paste defaults:
protected function getPluginDefaults(): array
{
return [
'scopeToTenant' => true,
'tenantRelationshipName' => null,
'tenantOwnershipRelationshipName' => null,
];
}Enables per-forResource configuration:
class YourPlugin implements Plugin
{
use HasNavigation;
use WithMultipleResourceSupport;
}
// Usage:
$plugin
->forResource(UserResource::class)
->navigationLabel('Users')
->forResource(PostResource::class)
->navigationLabel('Posts');- Add support for pages
- ...features you want to see? Open an issue