Looking to hire Laravel developers? Try LaraJobs

laravel-feature-flags maintained by chemaclass

Description
Agnostic feature flag management for Laravel with optional admin UI and per-scope overrides.
Last update
2026/05/15 16:06 (dev-main)
License
Downloads
3

Comments
comments powered by Disqus

laravel-feature-flags

CI Packagist Version Packagist Downloads PHP Laravel Coverage Live demo License

A full feature-flag toolkit for Laravel that needs nothing but your database. Simple on/off toggles when that's all you want — attribute targeting, percentage rollouts, A/B variants, per-environment values, and a Blade admin UI when you need more.

Drop it in any Laravel app. No external service, no vendor lock-in, no infra to run.

Live demo & feature tour — how it works, the admin UI, and every feature at a glance.

if (FeatureFlag::isEnabled('new-dashboard')) {
    // ship it
}

Install

composer require chemaclass/laravel-feature-flags
php artisan vendor:publish --tag=feature-flags
php artisan migrate

Auto-registers itself. Publishes the config, migration, admin view, and admin routes. See installation for piece-by-piece publishing.

Quickstart

1. Create a flag — from the admin UI at /admin/feature-flags, the CLI, or code:

php artisan flag:create new-dashboard --value=1

2. Check it anywhere:

use Chemaclass\FeatureFlags\Facades\FeatureFlag;

if (FeatureFlag::isEnabled('new-dashboard')) {
    // gated code
}

3. In Blade:

@feature('new-dashboard')
    <x-new-dashboard />
@else
    <x-legacy-dashboard />
@endfeature

4. Guard a route:

Route::get('/dashboard', DashboardController::class)
    ->middleware('feature.enabled:new-dashboard');

That's the whole loop. Everything below is optional power you can adopt when you need it.

Cookbook

Each recipe is copy-paste; follow the link for the full reference.

use Chemaclass\FeatureFlags\Contracts\FeatureKey;

enum AppFeature: string implements FeatureKey
{
    case NewDashboard = 'new-dashboard';

    public function key(): string { return $this->value; }
}

FeatureFlag::isEnabled(AppFeature::NewDashboard);

Generate this enum from your existing flags: php artisan flag:generate. → usage

// $scopeId is any string your app decides on. A scoped row beats the global one.
FeatureFlag::isEnabled('new-dashboard', $team->id);

A FeatureScopeResolver can resolve the scope automatically for the middleware and Blade. → scopes

FeatureFlag::updateOrCreate(['key' => 'new-billing', 'scope_id' => null], [
    'value' => false,
    'rules' => [
        ['when' => [['attr' => 'plan', 'op' => 'eq', 'value' => 'pro']], 'then' => true],
    ],
]);

FeatureFlag::isEnabled('new-billing', $userId, ['plan' => 'pro']); // true

Operators: eq, neq, in, not_in, gt, gte, lt, lte, contains, starts_with, ends_with. → usage

FeatureFlag::updateOrCreate(['key' => 'new-checkout', 'scope_id' => null],
    ['value' => true, 'rollout_percentage' => 30]);
// enabled for a stable ~30% of scopes

usage

$v = FeatureFlag::variant('homepage', $userId);
$v?->name;    // 'control' | 'blue'
$v?->payload; // per-variant blob

usage

FeatureFlag::allEnabled(['new-dashboard', 'beta-search'], $userId);
// ['new-dashboard' => true, 'beta-search' => false]
  • Environments — same key, different value per env. → usage
  • Prerequisites — a flag requires others to be on. → usage
  • Kill-switch — force keys off via FEATURE_FLAGS_KILL_SWITCH. → usage
  • Audit log — persist every toggle + admin history. → extending
  • Caching — request memoization always on; set a cache store for cross-request + real-time invalidation. → configuration

Enable the API (feature-flags.api.enabled), then evaluate flags over HTTP — targeting rules, rollout and variants all apply:

import { createFeatureFlags } from './vendor/feature-flags.js'; // php artisan vendor:publish --tag=feature-flags-js

const ff = createFeatureFlags({ endpoint: '/feature-flags/api/evaluate', scope: user.id, context: { plan: user.plan } });
await ff.load();
ff.isEnabled('new-dashboard'); // bool
ff.variant('homepage');        // { name, payload } | null

api

php artisan flag:list --scope=team-1
php artisan flag:create beta-search --value=0 --hint="WIP"
php artisan flag:toggle new-dashboard
php artisan flag:stale --days=30         # flags safe to delete
php artisan flag:generate                # typed enum from your keys
php artisan flag:sync --prune            # reconcile from a versioned file (config-as-code)
php artisan flag:stats                   # exposure counts per flag/variant (analytics)

usage

Admin UI

Visit /admin/feature-flags (configurable, gated by web+auth by default): flags grouped by key, sliding toggles, inline editing, scope overrides, dark mode. → admin-ui

What you get

Everything runs on your existing database — no service to run, no vendor lock-in. In one package:

  • Evaluation — boolean, per-scope overrides, attribute targeting rules, percentage rollout, time windows, prerequisites, and a global kill-switch.
  • Experiments & config — weighted multivariate variants with per-variant payloads.
  • Environments — the same key resolving differently per environment.
  • Delivery — facade, @feature Blade directives, route middleware, Artisan commands, and a JSON HTTP API with a zero-dependency JS client for frontends and other services.
  • Operations — request + cross-request caching with real-time invalidation, lifecycle events, an opt-in audit log with admin history, exposure analytics, stale-flag detection, typed enum codegen, and config-as-code sync.
  • Admin UI — a published Blade page, no build step.

It's a self-hosted toolkit for a Laravel app — not a polyglot SaaS with an experimentation stats engine and multi-language SDKs. If you need that, use a hosted platform. Evaluating alternatives (Pennant, Unleash, Flagsmith, LaunchDarkly)? Check their current docs — capabilities and pricing tiers change.

Already on Pennant? The optional bridge lets Feature::active() resolve through this package, so you can adopt it without rewriting call sites.

Documentation

Topic Link
Installation & setup installation.md
Configuration reference configuration.md
Defining, checking, targeting & variants usage.md
Scope resolvers scopes.md
Middleware guard middleware.md
HTTP API & JS client api.md
Exposure analytics analytics.md
Admin UI admin-ui.md
Custom repository, audit log, storage extending.md
Laravel Pennant bridge pennant.md
Testing testing.md
Recipes & patterns recipes.md
Architecture overview architecture.md

Stability

1.0 and stable — semver from here on, 100% test coverage enforced in CI. Pin with composer require chemaclass/laravel-feature-flags:^1.0.

Requirements

  • PHP ^8.3
  • Laravel ^11.0 || ^12.0

This package pulls in illuminate/* and nothing more. Any advisories from composer audit come transitively from the Laravel framework your app already ships — keep Laravel on its latest patch release to pick up fixes.

Contributing

See CONTRIBUTING.md for local dev setup, the Docker demo app, the test suites, formatting, and the release.sh flow.

License

MIT.