e0ipso / feature_flags
A module for feature flags in Drupal.
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
Type:drupal-module
pkg:composer/e0ipso/feature_flags
Requires (Dev)
- drupal/coder: ^8
- mglaman/phpstan-drupal: ^2.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^2.1
- phpstan/phpstan-deprecation-rules: ^2.0
README
Client-side feature flags that work with Varnish, CDN, and BigPipe. Resolve variants in JavaScript, not PHP.
What This Module Does
Create feature flags with multiple variants. Configure percentage rollouts and conditions. Everything resolves client-side so your page cache stays fast.
Perfect for high-traffic sites using aggressive caching (Varnish, CDN, BigPipe).
Pairs beautifully with ab_tests for A/B testing with metrics.
See It In Action
Video Walkthrough
coin-flip-demo.webm
👆 30-second walkthrough showing the complete setup and resolution process
Step-by-Step with Screenshots
1. Enable Settings (Debug + Persistence)
Enable debug mode to see resolution in console. Enable persistence to cache decisions in localStorage.
2. Create Your Feature Flag
Define basic info: label, machine name, description.
3. Add Variants (Minimum 2, JSON Values)
Each variant has a label and JSON value. CodeMirror editor included.
Example: "Heads, Tails, or Edge" coin flip
- Heads:
{"result": "heads", "color": "#FFD700"} - Tails:
{"result": "tails", "color": "#C0C0C0"} - Edge:
{"result": "edge", "color": "#FF6B6B"}(rare!)
4. Configure Percentage Rollout
48% Heads, 48% Tails, 4% Edge. Percentages must sum to 100%.
5. Resolve in JavaScript
const result = await Drupal.featureFlags.resolve('coin_flip'); console.log(result.result); // { result: "heads", color: "#FFD700" } console.log(result.variant.label); // "Heads"
Console Output:
Debug mode shows the full resolution process. Result is cached in localStorage for consistency.
Installation
composer require e0ipso/feature_flags drush pm:enable feature_flags -y drush cache:rebuild
Core Concepts
Variants
Each flag has 2 or more variants with JSON values. Think of variants as the possible outcomes.
{"enabled": true, "theme": "dark"}
{"enabled": false, "theme": "light"}
Algorithms
Algorithms decide which variant a user gets. The Percentage Rollout algorithm distributes users by percentage (e.g., 25% variant A, 75% variant B).
How to create a custom algorithm
-
PHP Plugin:
src/Plugin/DecisionAlgorithm/MyAlgorithm.php- Extends
DecisionAlgorithmPluginBase - Defines admin configuration form
- Returns JavaScript settings
- Extends
-
JavaScript Class:
js/algorithm/MyAlgorithm.js- Extends
BaseAlgorithm - Implements
decide(context)method - Returns selected variant
- Extends
-
Library: Add to
feature_flags.libraries.yml
/** * @DecisionAlgorithm( * id = "my_algorithm", * label = @Translation("My Custom Algorithm"), * js_library = "feature_flags/algorithm.my_algorithm", * js_class = "MyAlgorithm" * ) */ class MyAlgorithm extends DecisionAlgorithmPluginBase { public function decide(array $context): Variant { // Your logic here return $selectedVariant; } }
// js/algorithm/MyAlgorithm.js class MyAlgorithm extends Drupal.featureFlags.BaseAlgorithm { decide(context) { // Your client-side logic return this.featureFlag.variants[0]; } }
Conditions
Conditions filter when an algorithm applies. Target specific users, tiers, or custom context.
Built-in conditions:
- User ID: Match specific user IDs
- User Tier: Match tier values (free, premium, etc.)
How to provide custom context
Listen to the featureFlags:provideContext event:
document.addEventListener('featureFlags:provideContext', (event) => { // Add your custom context event.detail.addContext('user_tier', drupalSettings.user.tier); event.detail.addContext('region', 'US-West'); event.detail.addContext('subscription_level', 'premium'); // Async context supported event.detail.addContext('features', await fetchUserFeatures()); });
Then create conditions that evaluate your custom context:
/** * @AlgorithmCondition( * id = "region", * label = @Translation("Region"), * context_key = "region" * ) */ class RegionCondition extends AlgorithmConditionPluginBase { public function evaluate(array $context): bool { return in_array($context['region'] ?? '', $this->configuration['allowed_regions']); } }
Use With ab_tests for A/B Testing
The feature_flags module controls rollout. The ab_tests module tracks metrics.
Example: Progressive checkout rollout with conversion tracking
// 1. Resolve which checkout version to show const checkout = await Drupal.featureFlags.resolve('new_checkout'); if (checkout.result.enabled) { // 2. Show new checkout loadNewCheckout(); // 3. Track experiment with ab_tests Drupal.behaviors.abTests.trackExperiment('checkout_test', { variant: checkout.variant.label, user_id: drupalSettings.user.uid, }); // 4. Track conversion on purchase document.querySelector('.purchase-button').addEventListener('click', () => { Drupal.behaviors.abTests.trackConversion('checkout_test', 'purchase'); }); }
When to use each:
| Need | Use | Why |
|---|---|---|
| Gradual rollout (10% → 50% → 100%) | feature_flags |
Percentage control |
| Track metrics & conversions | ab_tests |
Analytics |
| Experiment + rollout | Both together | Best of both worlds |
Requirements
- Drupal 10.3+ or 11.x
- PHP 8.2+
- Modern browser with ES6 support
Testing
Test Commands
# PHP tests vendor/bin/phpunit web/modules/contrib/feature_flags # JavaScript tests cd web/modules/contrib/feature_flags npm test # E2E tests npm run e2e:test # Code quality vendor/bin/phpstan analyse web/modules/contrib/feature_flags vendor/bin/phpcs --standard=Drupal web/modules/contrib/feature_flags npm run js:check
Documentation
- docs/diagrams.md - Mermaid diagram source code
License
GPL v2 or later




