Skip to content

Latest commit

 

History

41 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🚀 vue-feature-gates

Modern feature flags for Vue 3: plugin + composable, type-safe, Pinia persistence, cross-tab sync, zero dependencies.

Perfect for serious Vue 3 applications that need production-ready A/B testing and feature rollout management.

npm version Vue 3 TypeScript License: MIT

Features

  • Type-safe - Full TypeScript support with generics
  • Lightweight - Zero dependencies (Pinia optional)
  • Reactive - Built on Vue 3's reactivity system
  • Persistence - Optional localStorage/sessionStorage via Pinia
  • Cross-tab sync - Real-time synchronization across browser tabs
  • Simple API - Easy to use composable and plugin
  • Production ready - No any types, fully tested

Installation

npm install vue-feature-gates

With Persistence (Optional)

If you want to persist feature flags to localStorage:

npm install vue-feature-gates pinia

Get Started in 10 Seconds

// main.ts
app.use(createFeatureFlags({ newDashboard: false }));

// AnyComponent.vue
const { flags } = useFeatureFlags<{ newDashboard: boolean }>();

That's it! Now use flags.newDashboard in your templates. See below for full examples with persistence and cross-tab sync.

Quick Start

Basic Usage (No Persistence)

// main.ts
import { createApp } from "vue";
import { createFeatureFlags } from "vue-feature-gates";
import App from "./App.vue";

const app = createApp(App);

app.use(
	createFeatureFlags({
		newDashboard: false,
		betaMode: false,
		darkTheme: true,
	}),
);

app.mount("#app");

With Pinia Persistence

// main.ts
import { createApp } from "vue";
import { createPinia } from "pinia";
import { createFeatureFlags } from "vue-feature-gates";
import App from "./App.vue";

const app = createApp(App);
const pinia = createPinia();

// Install Pinia first
app.use(pinia);

// Install feature flags with persistence
app.use(
	createFeatureFlags(
		{
			newDashboard: false,
			betaMode: false,
			darkTheme: true,
		},
		{
			pinia,
			storage: {
				enabled: true,
				storageKey: "my-app-feature-flags",
				storage: localStorage, // or sessionStorage
			},
		},
	),
);

app.mount("#app");

Usage in Components

Using the Composable

<template>
	<div>
		<!-- Conditionally render components based on flags -->
		<DashboardV2 v-if="flags.newDashboard" />
		<DashboardV1 v-else />

		<!-- Show beta features -->
		<BetaBanner v-if="flags.betaMode" />

		<!-- Toggle flags -->
		<button @click="toggleDashboard">
			{{ flags.newDashboard ? "Use Old Dashboard" : "Try New Dashboard" }}
		</button>
	</div>
</template>

<script setup lang="ts">
import { useFeatureFlags } from "vue-feature-flags";

const { flags, enable, disable, set } = useFeatureFlags<{
	newDashboard: boolean;
	betaMode: boolean;
	darkTheme: boolean;
}>();

const toggleDashboard = () => {
	set("newDashboard", !flags.newDashboard);
};

// Or use convenience methods
const enableBeta = () => enable("betaMode");
const disableBeta = () => disable("betaMode");
</script>

Feature Flag Control Panel Example

<template>
	<div class="feature-flags-panel">
		<h3>Feature Flags Control</h3>
		<div v-for="(value, key) in flags" :key="key" class="flag-control">
			<label>
				{{ key }}
				<input type="checkbox" :checked="value" @change="set(key, !value)" />
			</label>
		</div>
	</div>
</template>

<script setup lang="ts">
import { useFeatureFlags } from "vue-feature-flags";

const { flags, set } = useFeatureFlags();
</script>

API Reference

createFeatureFlags(flags, options?)

Creates the Vue plugin for feature flags.

Parameters:

  • flags: Record<string, boolean> - Initial feature flag values
  • options?: FeatureFlagOptions - Optional configuration

Options:

interface FeatureFlagOptions {
	// Optional Pinia instance for persistence
	pinia?: Pinia;

	// Storage configuration
	storage?: {
		enabled?: boolean; // Enable persistence (default: false)
		storageKey?: string; // localStorage key (default: 'vue-feature-flags')
		storage?: Storage; // Storage API (default: window.localStorage)
	};
}

Example:

app.use(
	createFeatureFlags(
		{ newDashboard: false },
		{
			pinia,
			storage: {
				enabled: true,
				storageKey: "my-app-flags",
			},
		},
	),
);

useFeatureFlags<T>()

Composable to access and control feature flags.

Returns:

{
  flags: T;                                    // Reactive flags object
  isEnabled: (key: keyof T) => boolean;       // Check if flag is enabled
  set: (key: keyof T, value: boolean) => void; // Set flag value
  enable: (key: keyof T) => void;             // Enable a flag
  disable: (key: keyof T) => void;            // Disable a flag
}

Example:

const { flags, enable, disable, set, isEnabled } = useFeatureFlags<{
	newDashboard: boolean;
	betaMode: boolean;
}>();

// Check flag
if (isEnabled("newDashboard")) {
	console.log("New dashboard is enabled!");
}

// Enable/disable
enable("betaMode");
disable("betaMode");

// Set directly
set("newDashboard", true);

// Access reactive flags
console.log(flags.newDashboard); // true

Persistence

When you provide a Pinia instance and enable storage, feature flags automatically:

  • Save to localStorage on every change
  • Load from localStorage on app initialization
  • Sync across tabs in real-time via storage events

Example with sessionStorage:

app.use(
	createFeatureFlags(
		{ temporaryFeature: false },
		{
			pinia,
			storage: {
				enabled: true,
				storage: sessionStorage, // Cleared when tab closes
			},
		},
	),
);

Real-World Example

Here's a complete example showing A/B testing for a new dashboard:

// main.ts
import { createApp } from "vue";
import { createPinia } from "pinia";
import { createFeatureFlags } from "vue-feature-gates";
import App from "./App.vue";

const app = createApp(App);
const pinia = createPinia();

app.use(pinia);
app.use(
	createFeatureFlags(
		{
			newDashboard: false,
			advancedCharts: false,
			exportPDF: true,
		},
		{
			pinia,
			storage: {
				enabled: true,
				storageKey: "analytics-app-flags",
			},
		},
	),
);

app.mount("#app");
<!-- Dashboard.vue -->
<template>
	<div>
		<!-- A/B test: New vs Old Dashboard -->
		<DashboardV2 v-if="flags.newDashboard" />
		<DashboardV1 v-else />

		<!-- Conditional features -->
		<AdvancedCharts v-if="flags.advancedCharts" />
		<ExportButton v-if="flags.exportPDF" format="pdf" />
	</div>
</template>

<script setup lang="ts">
import { useFeatureFlags } from "vue-feature-flags";
import DashboardV1 from "./DashboardV1.vue";
import DashboardV2 from "./DashboardV2.vue";
import AdvancedCharts from "./AdvancedCharts.vue";
import ExportButton from "./ExportButton.vue";

const { flags } = useFeatureFlags<{
	newDashboard: boolean;
	advancedCharts: boolean;
	exportPDF: boolean;
}>();
</script>

Why vue-feature-gates?

There are several feature flag libraries for Vue, but vue-feature-gates is built specifically for modern Vue 3 applications:

Feature vue-feature-gates Traditional Solutions
Vue 3 Optimized ✅ Built on Composition API ❌ Often Vue 2 or compatibility mode
Type Safety ✅ Full TypeScript with generics ⚠️ Limited or no type inference
Pinia Integration ✅ Native Pinia store support ❌ Custom state management
Cross-tab Sync ✅ Real-time sync via storage events ❌ Manual implementation needed
Zero Dependencies ✅ Core has no dependencies ⚠️ Often brings heavy deps
Production Ready ✅ No any types, fully tested ⚠️ Varies
DX-First API ✅ Composable + plugin, minimal setup ⚠️ Often verbose configuration

Perfect for:

  • Vue 3 apps using Pinia for state management
  • Teams needing type-safe feature toggles
  • Applications requiring cross-tab synchronization
  • Projects that value zero-dependency core libraries

Playground

See it in action! The playground demonstrates all key features:

Playground Demo - Feature Flags in Action

Live demo showing reactive component toggling with feature flags

Try it yourself:

cd playground
npm install
npm run dev

The playground demonstrates:

  • Real-time flag toggling with a control panel
  • Conditional component rendering (v-if based on flags)
  • Persistence across page reloads (localStorage)
  • Cross-tab synchronization (open 2+ tabs and toggle flags)

TypeScript Support

Full TypeScript support with type inference:

// Define your flags shape
interface AppFeatureFlags {
	newDashboard: boolean;
	betaMode: boolean;
	darkTheme: boolean;
}

// Type-safe everywhere
const { flags, set } = useFeatureFlags<AppFeatureFlags>();

set("newDashboard", true); // OK
set("invalidFlag", true); // Type error
set("darkTheme", "yes"); // Type error (must be boolean)

Architecture

src/
├── types/          # TypeScript type definitions
├── stores/         # Pinia store factory
├── plugins/        # Plugin & persistence logic
├── composables/    # useFeatureFlags composable
├── utils/          # Constants & utilities
└── core/           # Core controller (framework-agnostic)

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT © Thibault Beaumont

Links


Made with ❤️ for the Vue.js community

About

vue-feature-gates allows you to control feature visibility at runtime in your Vue 3 applications. Instead of using environment variables or config files that require rebuilds, you can toggle features dynamically—perfect for progressive rollouts, A/B testing, beta programs, or environment-based configurations

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages