flagmint-vuejs-feature-flags
Flagmint Vue Feature Flags SDK
A lightweight and powerful feature flag SDK for Vue 2 and Vue 3 applications.
Supports:
✅ Client-side flag evaluation
✅ Segment targeting and rollout strategies
✅ Server-Sent Events (SSE) or HTTP long-polling
✅ Offline caching and preview mode
✅ Vue 2 and Vue 3 plugin, composables, and helpers
🔧 Installation
npm install flagmint-vuejs-feature-flags
🚀 Quick Start
Vue 2
// main.js
import Vue from 'vue';
import { createFlagmintPlugin } from 'flagmint-vuejs-feature-flags';
Vue.use(createFlagmintPlugin({
apiKey: 'your-api-key',
enableFlagmint: true,
context: { user_id: 'abc123', country: 'NG' },
transportMode: 'auto',
// shareConnection: true, // default when BroadcastChannel is available — one shared SSE stream; without it each client opens its own
autoRefresh: true,
previewMode: false
}));
new Vue({ render: h => h(App) }).$mount('#app');
Vue 3
// main.ts
import { createApp } from 'vue';
import App from './App.vue';
import { createFlagmintPlugin } from 'flagmint-vuejs-feature-flags';
const app = createApp(App);
app.use(createFlagmintPlugin({
apiKey: 'your-api-key',
enableFlagmint: true,
context: { user_id: 'abc123' },
transportMode: 'auto',
// shareConnection: true, // default when BroadcastChannel is available — one shared SSE stream; without it each client opens its own
previewMode: false
}));
app.mount('#app');
⚙️ API Overview
FlagClientOptions
interface FlagClientOptions {
apiKey: string;
enableFlagmint: boolean; // true to connect; false skips the network and evaluation cost
context?: Record<string, any>;
autoRefresh?: boolean;
refreshIntervalMs?: number;
persistContext?: boolean;
env?: string; // production | staging
enableOfflineCache?: boolean; // also persists signed rules when configSync is on
cacheTTL?: number;
transportMode?: 'auto' | 'sse' | 'long-polling';
configSync?: boolean; // local evaluation — download signed rules and run getFlag in-process (default false)
previewMode?: boolean;
onError?: (err: Error) => void;
deferInitialization?: boolean;
shareConnection?: boolean; // js-sdk shares one SSE stream across same-origin tabs/iframes. Default: true in browsers with BroadcastChannel (false when configSync is on).
}
🎯 Using Flags
✅ Recommended (Reactive Flags)
Subscribe to flag updates so your component re-renders automatically on every SSE push:
// Vue 3 Composition API
import { useFlagmint } from 'flagmint-vuejs-feature-flags';
const { getFlag, isReady } = useFlagmint();
const darkMode = computed(() => getFlag('dark-mode', false));
// Vue 2 (via mixin)
export default {
mixins: [useFlagsMixin],
mounted() {
console.log(this.flags['dark-mode']);
console.log(this.getFlag('dark-mode', false));
}
}
⚠️ Non-reactive (snapshot only, won't update on push)
const enabled = this.$flagmint.getFlag('dark-mode', false);
Use this only when you genuinely need a one-time read (e.g. inside a non-reactive utility function). For anything rendered in a template, use the reactive pattern above.
⏳ Await Initialization
$flagmintReady is a reactive boolean ref, not a Promise — watch it rather than awaiting it:
// Vue 2
this.$watch('$flagmintReady', (ready) => {
if (ready) { /* client is ready */ }
});
// Vue 3
import { useFlagmintReady } from 'flagmint-vuejs-feature-flags';
const isReady = useFlagmintReady(); // Ref<boolean>
watch(isReady, (ready) => {
if (ready) { /* client is ready */ }
});
If you need a one-time async wait (e.g. before an onMounted block runs), call the injected init() function instead — it returns a Promise that resolves once the client is ready:
const client = await this.$flagmintInit(); // Vue 2
🔌 Vue 2 Helpers
$flagmint access
export default {
async mounted() {
const client = await this.$flagmintInit();
const enabled = client.getFlag('dark-mode', false);
}
}
✅ Vue 2 Mixin
import { useFlagsMixin } from 'flagmint-vuejs-feature-flags/vue2/mixin/useFlagsMixin';
export default {
mixins: [useFlagsMixin],
mounted() {
console.log(this.flags['dark-mode']);
console.log(this.getFlag('dark-mode', false));
}
}
🔌 Vue 3 Helpers
Option A: Composition API (Recommended)
import { useFlagmint } from 'flagmint-vuejs-feature-flags';
export default {
setup() {
const { getFlag, isReady, trackError } = useFlagmint();
const feature = computed(() => getFlag('chat-enabled', false));
return { feature };
}
}
Option B: Injected
import { inject } from 'vue';
export default {
setup() {
const client = inject('__flagmint__');
const feature = client?.getFlag('chat-enabled');
return { feature };
}
}
📦 Local evaluation (configSync)
By default the SDK uses server evaluation: Flagmint computes flag values and streams the results. Set configSync: true to opt into local evaluation (config sync): the stream delivers signed rules (lease / fullConfig / delta), and getFlag() evaluates in-process.
Requires Flagmint API 1.7.0+ and flagmint-js-sdk 2.2.2+ (this Vue package depends on ^2.2.2). Passed through to the JS SDK like other FlagClientOptions.
createFlagmintPlugin({
apiKey: 'your-api-key',
enableFlagmint: true,
configSync: true, // local evaluation
enableOfflineCache: true, // persist rules between reloads (default true)
});
Notes:
- With
configSync: true,shareConnectiondefaults to false (rules are not shared across tabs yet). - Expired lease fail-closes to defaults only until the stream refreshes rules.
- Classic server evaluation remains the default if you omit
configSync.
🔗 Connection sharing (shareConnection)
Same-origin tabs and iframes that share an API key use one SSE connection when BroadcastChannel is available (handled by flagmint-js-sdk, not Vue-level leader election). Defaults to true in those browsers (and to false when configSync is on).
createFlagmintPlugin({
apiKey: 'your-api-key',
enableFlagmint: true,
shareConnection: true, // default in browsers with BroadcastChannel (unless configSync)
// shareConnection: false, // two clients / two contexts / two streams
});
Set shareConnection: false if two clients on the page must keep different evaluation contexts, or if the origin hosts untrusted documents.
Debugging Broadcast Channels in DevTools
To verify that multiple frames collapse to a single stream:
- Open Chrome/Edge DevTools → Application → Background Services → Broadcast Channels.
- Reload the page; you should see the
flagmint-share:<apiKey>channel. - In Network, confirm only one EventSource/SSE stream opens while all views stay in sync.
🧩 Feature Component
In Vue templates, use the <BoolFeatureGate> component:
<template>
<BoolFeatureGate featureKeys="['dark-mode']">
<div>Dark mode is enabled!</div>
</BoolFeatureGate>
</template>
<script setup lang="ts">
import { BoolFeatureGate } from 'flagmint-vuejs-feature-flags/vue3/Feature';
</script>
feature-keys accepts a single string or an array of strings. If multiple keys are provided, all must be enabled.
⚠️ Important: Never call
client.destroy()from within a component or composable.destroy()tears down the shared SSE connection for the entire application. If you're building a custom integration aroundFlagClient, only ever callclient.subscribe()and its returned unsubscribe function for component-level cleanup.
🧪 Preview Mode (No Network)
Enable previewMode: true in FlagClientOptions to evaluate flags locally only:
- No API key needed
- Useful for SDK testing, Storybook, or static environments
createFlagmintPlugin({
previewMode: true,
enableFlagmint: false,
context: { user_id: 'test' }
});
You can then load flags directly:
flagClient.setFlags([flag1, flag2], segmentsById);
⚠️ A console warning appears in development when previewMode is active.
🧠 Evaluation Logic
-
Operators:
eq,neq,in,nin,gt,lt,exists,not_exists -
Segment references and rule groups
-
Rollout strategies:
percentage— user hashes to percentilevariant— weighted multi-variant assignment
🔁 Realtime Updates
Using transportMode: 'sse' or 'auto', flags update live when changed.
With transportMode: 'auto', the SDK falls back to HTTP long-polling if SSE fails. With transportMode: 'sse', no fallback occurs.
📊 Application error tracking
Call trackError after a flag was served if that variation hurt the user. The JS SDK no-ops when Analytics Tracking is off for that flag. The server also drops those events.
// Vue 3
const { getFlag, trackError } = useFlagmint();
try {
await submitPayment();
} catch (error) {
trackError('checkout_redesign', error, { step: 'payment' });
throw error;
}
// Vue 2 mixin
this.trackError('checkout_redesign', error);
track(flagKey, eventName) uses the same pipe for custom metrics. Only kind: 'error' is counted in app error rate today.
📦 Versioning & Releases
This project follows Semantic Versioning. Releases are automated via GitHub Actions:
- Changes to
sdk/,package.json,rollup.config.js, ortsconfig.jsonon themainbranch trigger the release workflow - The workflow extracts the version from
package.json, builds the package, and publishes to npm - Git tags and GitHub releases are automatically created
To release a new version:
- Update the version in
package.json - Update CHANGELOG.md with your changes
- Push to
mainbranch - The workflow handles the rest
See CHANGELOG.md for version history and release notes.
🤝 Contributing
Contributions are welcome! Here's how to get started:
- Clone the repository
- Install dependencies:
npm install - Run tests:
npm test - Build the project:
npm run build - Create a feature branch:
git checkout -b feature/your-feature - Make your changes and commit
- Push to your fork and create a Pull Request
Development Tips:
- The SDK supports both Vue 2 and Vue 3 — test changes against both versions
- Keep bundle size in mind when adding dependencies
- Run tests before submitting PRs
- Never call
client.destroy()from a component, mixin, or composable — only the plugin/app-level teardown should own that call
🗂 Roadmap
- Segment evaluation
- Rollout strategies
- Preview/local-only mode
- Composables and mixins
- SSE + long-polling fallback
- Feature component
- SSR / Nuxt support
- Variant analytics
- Remote override via devtools
🐛 Troubleshooting
Flags not loading
- Ensure
apiKeyis valid and environment has network access - Check browser console for errors
- Verify
contextis properly set with required attributes - If using
previewMode: true, ensure you've calledflagClient.setFlags()
SSE connection fails
- With
transportMode: 'auto'the SDK falls back to long-polling - Check network connectivity and CORS settings
- Verify the API server exposes
GET /evaluator/v2/flags/stream - Check browser console for connection errors
Component re-renders not happening on flag updates
- Make sure you're using the reactive pattern (
useFlagmint()composable oruseFlagsMixin) rather than callingclient.getFlag()directly in acomputed. The client's internal flag state is not a Vue reactive object — only thesubscribe()-backed wrappers re-render correctly - If you unmounted and remounted a
<BoolFeatureGate>and updates stopped working app-wide, confirm nothing in your codebase callsclient.destroy()outside of app-level teardown
Stale flags in offline cache
- Clear localStorage or set
enableOfflineCache: false - Adjust
cacheTTLto control cache duration (in milliseconds) - Use
previewMode: truefor testing without network
Performance issues
- Limit the number of flags in targeting rules
- Use the
subscribe()-backed reactive pattern (via composable or mixin) instead of pollinggetFlag()in a loop - Consider lazy-loading flags for large applications
TypeScript issues
- Ensure
tsconfig.jsonincludessdk/inincludepaths - Check that
node_modulestypes are installed:npm install
📜 License
BSD 3-Clause License
Related Packages
Vue SDK for A vs B feature flags and experiments: composables, a provider compon...
Nuxt module for A vs B feature flags: one line in nuxt.config, auto-imported com...
Advanced and highly reusable Mobile UI Components built on Vue2 & You can define...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 2.1.1 | ^2.7.0 || | BSD-3-Clause | |
| 2.1.0 | ^2.7.0 || | BSD-3-Clause | |
| 2.0.0 | ^2.7.0 || | BSD-3-Clause | |
| 1.3.9 | ^2.7.0 || | BSD-3-Clause | |
| 1.3.8 | ^2.7.0 || | BSD-3-Clause | |
| 1.3.7 | ^2.7.0 || | BSD-3-Clause | |
| 1.3.6 | ^2.7.0 || | BSD-3-Clause | |
| 1.3.5 | ^2.7.0 || | MIT | |
| 1.3.4 | ^2.7.0 || | MIT | |
| 1.3.3 | ^2.7.0 || | MIT | |
| 1.3.2 | ^2.7.0 || | MIT | |
| 1.3.1 | ^2.7.0 || | MIT | |
| 1.3.0 | ^2.7.0 || | MIT | |
| 1.2.0 | ^2.7.0 || | MIT | |
| 1.1.27 | ^2.7.0 || | MIT | |
| 1.0.27 | ^2.7.0 || | MIT | |
| 1.0.26 | ^2.7.0 || | MIT | |
| 1.0.25 | ^2.7.0 || | MIT | |
| 1.0.24 | ^2.7.0 || | MIT | |
| 1.0.23 | ^2.7.0 || | MIT | |
| 1.0.22 | ^2.7.0 || | MIT | |
| 1.0.21 | ^2.7.0 || | MIT | |
| 1.0.20 | ^2.7.0 || | MIT | |
| 1.0.19 | ^2.7.0 || | MIT | |
| 1.0.18 | ^2.7.0 || | MIT | |
| 1.0.17 | ^2.7.0 || | MIT | |
| 1.0.16 | ^2.7.0 || | MIT | |
| 1.0.15 | ^2.7.0 || | MIT | |
| 1.0.14 | ^2.7.0 || | MIT | |
| 1.0.13 | ^2.7.0 || | MIT |
Showing the latest 30 of 43. See every release on npm
Pre-releases (9)
| Version | Released | Vue | License |
|---|---|---|---|
| 2.1.1-rc.6 | ^2.7.0 || | BSD-3-Clause | |
| 2.1.1-rc.5 | ^2.7.0 || | BSD-3-Clause | |
| 2.1.1-rc.4 | ^2.7.0 || | BSD-3-Clause | |
| 2.1.1-rc.3 | ^2.7.0 || | BSD-3-Clause | |
| 2.1.1-rc.2 | ^2.7.0 || | BSD-3-Clause | |
| 2.1.1-rc.1 | ^2.7.0 || | BSD-3-Clause | |
| 2.1.1-rc.0 | ^2.7.0 || | BSD-3-Clause | |
| 2.1.0-rc.1 | ^2.7.0 || | BSD-3-Clause | |
| 2.1.0-rc.0 | ^2.7.0 || | BSD-3-Clause |