liberusoftware/feature-flags-filament
A Filament admin panel integration for managing feature flags. Browse, toggle, and configure flags from a dedicated Filament resource, giving teams a simple UI to control feature rollouts without touching code or config files.
Treat this as a Filament presentation layer over Liberu's feature-flag domain package, not a standalone flag engine. You will almost certainly need the core feature-flags module installed and configured first; this package only supplies the Filament plugin, resource/page, and UI for toggling flags.
Minimal setup:
composer require liberusoftware/feature-flags-filament
Verify the dependency chain before writing code:
composer show liberusoftware/feature-flags-filament
composer show liberusoftware/feature-flags # or module-feature-flags
Then publish and migrate:
php artisan vendor:publish --tag="feature-flags-filament-config"
php artisan vendor:publish --tag="feature-flags-filament-migrations"
php artisan migrate
Register the plugin in your panel provider:
use Liberu\FeatureFlagsFilament\FeatureFlagsFilamentPlugin; // verify exact FQCN in src/
public function panel(Panel $panel): Panel
{
return $panel
->plugins([
FeatureFlagsFilamentPlugin::make(),
]);
}
Where to look first, in order:
composer.json — Filament major-version constraint, core module dependency, PHP version, autoload namespace.src/ — the plugin class and service provider. This tells you the real class names, publish tags, and whether migrations live here or in the core module.config/feature-flags-filament.php — navigation group, resource registration, tenancy, authorization hooks.resources/views/ — whether the UI is a custom page, a resource, or a widget.README.md and tests/ — the only reliable documentation for an unstarred package.First use case: create a flag named new-dashboard in the Filament UI, then gate a resource or navigation item:
public static function shouldRegisterNavigation(): bool
{
return Feature::active('new-dashboard');
}
If Feature is not the facade name in your core module, check the core package's src/Facades directory before copying snippets.
Register flags in code, toggle them in the UI. The Filament screen is an operator interface, not the source of truth. Define flags in an enum or config file and sync them to the database:
enum Flags: string
{
case NewDashboard = 'new-dashboard';
case BetaBilling = 'beta-billing';
}
Then seed or sync:
foreach (Flags::cases() as $flag) {
FeatureFlag::firstOrCreate(['name' => $flag->value]);
}
Avoid letting operators create arbitrary flag names in production; that is how you get typo-flagged dead code.
Gate Filament surface area with closures, not boot-time checks. Filament evaluates visibility at render time when you use closures, which means a flag toggle can take effect on the next request:
Resource::make()
->visible(fn () => Feature::active(Flags::BetaBilling->value));
TextInput::make('vat_number')
->visible(fn () => Feature::active('eu-vat'));
Action::make('recalculate')
->disabled(fn () => ! Feature::active('new-billing-engine'));
For whole resources, canAccess() and shouldRegisterNavigation() are the correct hooks. Do not conditionally call ->resources([...]) based on a flag unless you accept that the panel's component cache must be cleared on every toggle.
Wrap flag checks in a cached service. A flag check per row, per field, or per navigation item becomes a query per check if the adapter reads from the database. Centralize:
class Flags
{
public function active(string $flag): bool
{
$tenant = Filament::getTenant()?->getKey() ?? 'global';
return Cache::remember(
"flag:{$tenant}:{$flag}",
now()->addMinutes(5),
fn () => Feature::active($flag)
);
}
}
Invalidate in an observer on the flag model, and key the cache by tenant if you use Filament tenancy.
Use the UI for environment-specific overrides, not for permanent decisions. A common workflow: flags default to false in config, QA enables them through the Filament panel, and production enables them only after release. Record who toggled what — if the package does not ship an audit log, add an observer on the flag model that writes to your activity log.
Respect tenancy. If your panel uses ->tenant(Team::class), check whether the plugin and the core module scope flags per tenant. If they do not, you must scope them yourself or accept global flags. In a multi-tenant panel, a global flag toggle from one tenant's admin UI is a security and product problem.
Testing pattern. Do not drive the Filament UI in unit tests to flip flags. Seed the flag state directly and use RefreshDatabase:
protected function setUp(): void
{
parent::setUp();
FeatureFlag::create(['name' => 'beta-billing', 'enabled' => true]);
Cache::flush();
}
If the core module exposes a fake or Feature::fake(), use it for pure unit tests; use the database path for integration tests that exercise the Filament resource.
Authorization. Add a policy for the flag resource or a gate in the plugin config so only trusted roles can toggle flags. The default behavior of a Filament resource is "any panel user who can reach it," which is rarely what you want for production flags.
NOASSERTION license. Treat this as vendor code you own. Pin an exact commit or version in composer.json, vendor-review the license with legal, and be prepared to fork. Do not assume upstream fixes.filament/filament constraint in composer.json before upgrading Filament. A plugin built for Filament v3 will not silently work on v4; panel registration and resource APIs changed.migrate.flag:* keys.php artisan filament:cache-components and php artisan optimize can freeze conditional resource registration. If a flag "doesn't work" in production but works locally, clear Filament's caches first.Feature::active() inside a service provider's boot() method runs before the database may be available (e.g., during migrate or package discovery). Prefer closures and defer checks to request handling.canViewAny, canCreate, and canUpdate behavior. Add a policy before exposing the panel to non-admin users.php artisan vendor:publish interactively and look for the package's provider to discover the real tags; incorrect tags fail silently.Liberu\... and module-specific namespaces across versions. Confirm the plugin FQCN from src/ rather than copying from a blog post.php artisan about shows discovered packages; php artisan filament:about (if available in your Filament version) shows panel plugins. If the plugin does not appear, registration failed before any flag logic ran.->using()-style resource override, and any registered widget. If the package exposes a custom page instead of a resource, you can usually replace it via config without forking.composer update as a code review. Read the diff on src/ and the migrations, not just the changelog.How can I help you explore Laravel packages today?