flagmint-vuejs-feature-flags
v1.3.9
Published
A Vue.js SDK for managing feature flags in Flagmint applications, supporting both Vue 2 and Vue 3.
Maintainers
Readme
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
✅ WebSocket or HTTP long-polling
✅ Offline caching and preview mode
✅ Vue 2 and Vue 3 plugin, composables, and helpers
✅ Cross-Iframe & Multi-Tab Socket Optimization (Leader Election)
🔧 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',
context: { user_id: 'abc123', country: 'NG' },
transportMode: 'auto',
autoRefresh: true,
previewMode: false,
// Optional Cross-Iframe Optimization Parameters:
syncCrossIframes: true,
syncNamespace: 'wsd_app_prod'
}));
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',
context: { user_id: 'abc123' },
transportMode: 'auto',
previewMode: false,
// Optional Cross-Iframe Optimization Parameters:
syncCrossIframes: true,
syncNamespace: 'wsd_app_prod'
}));
app.mount('#app');⚙️ API Overview
FlagClientOptions
interface FlagClientOptions {
apiKey: string;
context?: Record<string, any>;
autoRefresh?: boolean;
refreshIntervalMs?: number;
persistContext?: boolean;
env?: string; // production | staging
enableOfflineCache?: boolean;
cacheTTL?: number;
transportMode?: 'auto' | 'websocket' | 'long-polling';
previewMode?: boolean;
onError?: (err: Error) => void;
// Framework Extension Parameters
deferInitialization?: boolean;
syncCrossIframes?: boolean; // Collapses multiple nested iframes/tabs down to 1 open socket
syncNamespace?: string; // Unique isolation scope descriptor to prevent account data bleed
}🎯 Using Flags
Cross-Iframe & Multi-Tab Connection Sharing
When embedding your Vue application multiple times on a single webpage using nested iframe configurations (or when a user leaves your app open across multiple simultaneous browser windows), initializing separate WebSockets or SSE streams per view causes significant connection overhead.
Setting syncCrossIframes: true leverages a reactive browser mesh network strategy via the BroadcastChannel API and Page Visibility API to solve this issue seamlessly.
🔒 Security Notice: Always match your syncNamespace parameter configuration with your underlying profile or customer session parameters (e.g. syncNamespace: currentSiteId). This isolates the messaging context and prevents separate client scopes from accidentally reading or blending flag variants in multi-tenant environments.
### ✅ Recommended (Reactive Flags)
Subscribe to flag updates so your component re-renders automatically on every WebSocket push:
```ts
// 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 } = 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 };
}
}💡 Debugging Broadcast Channels in DevTools
To verify that multiple frames are collapsing down to a single socket on your local device environment:
- Open Chrome/Edge DevTools (F12) and head to the Application tab.
- In the left panel section menu, find Background Services and click on Broadcast Channels.
- Reload your project page setup. You will see your namespace registration signature.
- Click on your project Network panel tab. Confirm that switching windows or updating your cloud variables updates all views concurrently while creating exactly one websocket initialization record track.
🧩 Feature Component
In Vue templates, use the <BoolFeatureGate> component:
is for boolean flags only, and string/number/JSON flags should be read via getFlag() directly and branched on with v-if.
<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 WebSocket 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,
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_existsSegment references and rule groups
Rollout strategies:
percentage— user hashes to percentilevariant— weighted multi-variant assignment
🔁 Realtime Updates
Using transportMode: 'websocket' or 'auto', flags update live when changed.
Fallback to polling if WebSocket fails.
📦 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
- [x] Segment evaluation
- [x] Rollout strategies
- [x] Preview/local-only mode
- [x] Composables and mixins
- [x] WebSocket + fallback
- [x] 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()
WebSocket connection fails
- The SDK automatically falls back to long-polling
- Check network connectivity and CORS settings
- Verify the API server supports WebSocket connections
- 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
