stephenjude/filament-feature-flags
Filament Feature Flags is a Filament plugin for managing Laravel Pennant feature flags from your admin panel. Create and toggle flags, define scopes and conditions, and roll out features per user, team, or custom segment without code changes. Integrates with Filament resources and dashboards.
composer require stephenjude/filament-feature-flags
php artisan vendor:publish --tag="filament-feature-flags-migrations"
php artisan migrate
If you want durable flag state, also publish Pennant’s migrations and switch its driver to database; Pennant’s default cache store is not a good fit for flags managed from an admin panel.
Register the plugin in your Filament panel provider:
use Stephenjude\FilamentFeatureFlags\FilamentFeatureFlagsPlugin;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->plugin(FilamentFeatureFlagsPlugin::make());
}
Define your first feature with Pennant in a service provider:
use Laravel\Pennant\Feature;
Feature::define('new-checkout', fn (User $user) => $user->isBetaTester());
Then open the Feature Flags resource in Filament, create/enable the flag or segment, and evaluate it in application code:
if (Feature::for($user)->active('new-checkout')) {
// show the new flow
}
Where to look first:
FeatureFlagResource and any segment relation manager: this is the day-to-day UI.app/Providers/AppServiceProvider.php: where your Pennant feature definitions live.A good first use case is an internal beta: define the feature, create a segment for staff or a specific plan, enable it through the Filament UI, verify with a real test user, then widen the rollout.
Feature::define should be the source of truth for resolution; the admin UI should toggle or segment that feature.Feature::define('checkout', fn (User $user) => match (true) {
$user->isInternal() => 'v2',
$user->plan === 'pro' => 'v2',
default => 'v1',
});
Feature::for($user)->active('new-checkout');
Do not rely on auth()->user() inside queued work; there may be no authenticated user at runtime.
final class Flags
{
public static function checkout(User $user): string
{
return Feature::for($user)->value('checkout') ?? 'v1';
}
}
@feature directive where it fits; for route-level gating, use middleware or policies rather than scattering checks.Feature::for($user)->activate('checkout');
Feature::flushCache();
Feature::flushCache() after programmatic changes and test with the same cache driver as production.cache, which is not durable. If flags must survive cache clears, use the database driver and publish Pennant’s migrations. Confirm which tables belong to Pennant and which belong to this package.Feature::define, and run php artisan config:clear after adding definitions. Stale config caches are a common reason a flag “disappears” from the UI.Feature::active() without an explicit scope may resolve against the current user or a default scope; in console and queue contexts that can be null. Prefer Feature::for($user).php artisan vendor:publish interactively to inspect available tags instead of copying old README commands blindly.Feature::for($user)->value('flag') in Tinker, inspect the features table and the package’s flag/segment tables, and flush cache before assuming a bug.activateForEveryone / deactivateForEveryone for global states. Keep custom evaluation behind an interface so tests can fake it.How can I help you explore Laravel packages today?