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.
- 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
anytypes, fully tested
npm install vue-feature-gatesIf you want to persist feature flags to localStorage:
npm install vue-feature-gates pinia// 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.
// 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");// 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");<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><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>Creates the Vue plugin for feature flags.
Parameters:
flags:Record<string, boolean>- Initial feature flag valuesoptions?: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",
},
},
),
);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); // trueWhen 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
},
},
),
);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>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 | |
| 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 | |
| Production Ready | ✅ No any types, fully tested |
|
| DX-First API | ✅ Composable + plugin, minimal setup |
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
See it in action! The playground demonstrates all key features:
Live demo showing reactive component toggling with feature flags
Try it yourself:
cd playground
npm install
npm run devThe 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)
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)src/
├── types/ # TypeScript type definitions
├── stores/ # Pinia store factory
├── plugins/ # Plugin & persistence logic
├── composables/ # useFeatureFlags composable
├── utils/ # Constants & utilities
└── core/ # Core controller (framework-agnostic)
Contributions are welcome! Please feel free to submit a Pull Request.
MIT © Thibault Beaumont
Made with ❤️ for the Vue.js community
