Skip to main content

Vue SDK Integration

3. Install the SDK

# Using npm
npm install @flagpole/client-vue socket.io-client

# Using yarn
yarn add @flagpole/client-vue socket.io-client

4. Initialize in Your Application

// main.ts
import { createApp } from "vue";
import { createFlagpole } from "@flagpole/client-vue";
import App from "./App.vue";

const app = createApp(App);

app.use(
createFlagpole({
apiKey: "fp_live_your_api_key",
environments: ["development"], // optional, if nothing is passed, then all environments will be shown (production, staging and development)
})
);

app.mount("#app");

Method 2: Using the Provider Component

<!-- App.vue -->
<template>
<FeatureFlagProvider
api-key="fp_live_your_api_key"
:environments="['development']"
>
<FeatureComponent />
</FeatureFlagProvider>
</template>

<script setup lang="ts">
import { FeatureFlagProvider } from "@flagpole/client-vue";
import FeatureComponent from "./components/FeatureComponent.vue";
</script>

5. Use Feature Flags

Composition API Usage

<template>
<div>
<h2>Feature Flags Test</h2>

<!-- Loading state -->
<div v-if="isLoading" class="loading">Loading flags...</div>

<!-- Error state -->
<div v-if="error" class="error">Error: {{ error.message }}</div>

<!-- Using individual flag composable -->
<div v-if="isNewFeatureEnabled">
<NewFeature />
</div>

<!-- Using directive -->
<div v-feature-flag="'premiumFeature'">
<PremiumContent />
</div>

<!-- Display all available flags -->
<h3>All Flags:</h3>
<div v-for="(flag, name) in flags" :key="name" class="flag-item">
<strong>{{ name }}:</strong>
<span :class="flag.isEnabled ? 'enabled' : 'disabled'">
{{ flag.isEnabled ? "Enabled" : "Disabled" }}
</span>
</div>
</div>
</template>

<script setup lang="ts">
import { useFeatureFlag, useFeatureFlags } from "@flagpole/client-vue";
import NewFeature from "./NewFeature.vue";
import PremiumContent from "./PremiumContent.vue";

// Get all feature flags
const { flags, isLoading, error, isFeatureEnabled } = useFeatureFlags();

// Get a specific feature flag
const isNewFeatureEnabled = useFeatureFlag("newFeature");

// Use the function from useFeatureFlags for programmatic checks
const handleButtonClick = () => {
if (isFeatureEnabled("buttonFeature")) {
console.log("Button feature is enabled");
}
};
</script>

<style scoped>
.loading {
color: #666;
}
.error {
color: red;
}
.enabled {
color: green;
}
.disabled {
color: gray;
}
.flag-item {
margin: 5px 0;
}
</style>

Directive Usage

<template>
<div>
<!-- Basic directive usage -->
<div v-feature-flag="'newFeature'">
<h3>New Feature Content</h3>
</div>

<!-- Inverted logic with :not modifier -->
<div v-feature-flag:not="'maintenanceMode'">
<p>Application is running normally</p>
</div>

<!-- Multiple flags with computed properties -->
<div v-if="showAdvancedUI">
<AdvancedDashboard />
</div>
</div>
</template>

<script setup lang="ts">
import { computed } from "vue";
import { useFeatureFlag } from "@flagpole/client-vue";

const isAdvancedUIEnabled = useFeatureFlag("advancedUI");
const isPremiumUser = useFeatureFlag("premiumAccess");

const showAdvancedUI = computed(
() => isAdvancedUIEnabled.value && isPremiumUser.value
);
</script>

Global Method Usage (Plugin Only)

<template>
<div>
<!-- Using global method when plugin is installed -->
<button
v-if="$isFeatureEnabled('globalFeature')"
@click="handleGlobalAction"
>
Global Feature Action
</button>
</div>
</template>

<script setup lang="ts">
import { getCurrentInstance } from "vue";

const instance = getCurrentInstance();

const handleGlobalAction = () => {
// Access via global properties
if (
instance?.appContext.app.config.globalProperties.$isFeatureEnabled(
"advancedMode"
)
) {
console.log("Advanced mode is enabled");
}
};
</script>

Available APIs

Composables

useFeatureFlags

Returns all feature flags and service state:

const {
flags, // ComputedRef<Record<string, FeatureFlag>>
isLoading, // ComputedRef<boolean>
error, // ComputedRef<Error | null>
isFeatureEnabled, // (flagName: string) => boolean
} = useFeatureFlags();

useFeatureFlag

Returns the state of a specific feature flag:

const isEnabled = useFeatureFlag("flagName"); // ComputedRef<boolean>

Directive

Use the v-feature-flag directive to conditionally show/hide elements:

<!-- Basic usage -->
<div v-feature-flag="'featureName'">Content</div>

<!-- Inverted logic -->
<div v-feature-flag:not="'maintenanceMode'">
Content when not in maintenance
</div>

Plugin

Install globally with the plugin:

import { createFlagpole } from "@flagpole/client-vue";

app.use(
createFlagpole({
apiKey: "your-api-key",
environments: ["development"],
})
);

Provider Component

Alternative setup using the provider component:

<FeatureFlagProvider api-key="your-api-key" :environments="['development']">
<YourApp />
</FeatureFlagProvider>

Advanced Usage Patterns

Custom Composable for Complex Logic

// composables/useNavigation.ts
import { computed } from "vue";
import { useFeatureFlag } from "@flagpole/client-vue";

export function useNavigation() {
const newNavEnabled = useFeatureFlag("newNavigation");
const adminPanelEnabled = useFeatureFlag("adminPanel");
const betaFeaturesEnabled = useFeatureFlag("betaFeatures");

const navigationItems = computed(() => {
const items = [
{ label: "Home", path: "/" },
{ label: "About", path: "/about" },
];

if (newNavEnabled.value) {
items.push({ label: "Dashboard", path: "/dashboard" });
}

if (adminPanelEnabled.value) {
items.push({ label: "Admin", path: "/admin" });
}

if (betaFeaturesEnabled.value) {
items.push({ label: "Beta", path: "/beta" });
}

return items;
});

return {
navigationItems,
hasAdvancedFeatures: computed(
() => adminPanelEnabled.value || betaFeaturesEnabled.value
),
};
}

Vue Router Integration

// router/index.ts
import { createRouter, createWebHistory } from "vue-router";

// Feature flag guard
function featureFlagGuard(flagName: string, redirectTo: string = "/") {
return (to: any, from: any, next: any) => {
const app = to.matched[0]?.instances?.default?.$parent?.$root;
const isEnabled = app?.$isFeatureEnabled?.(flagName);

if (isEnabled !== false) {
next();
} else {
next(redirectTo);
}
};
}

const routes = [
{
path: "/beta-feature",
component: () => import("./views/BetaFeature.vue"),
beforeEnter: featureFlagGuard("betaAccess", "/coming-soon"),
},
{
path: "/admin",
component: () => import("./views/AdminPanel.vue"),
beforeEnter: featureFlagGuard("adminPanel", "/unauthorized"),
},
];

export default createRouter({
history: createWebHistory(),
routes,
});

A/B Testing Implementation

<template>
<div>
<component :is="checkoutComponent" @purchase="handlePurchase" />
</div>
</template>

<script setup lang="ts">
import { computed } from "vue";
import { useFeatureFlags } from "@flagpole/client-vue";
import CheckoutV1 from "./CheckoutV1.vue";
import CheckoutV2 from "./CheckoutV2.vue";
import CheckoutV3 from "./CheckoutV3.vue";

const { flags } = useFeatureFlags();

const checkoutComponent = computed(() => {
const experimentFlag = flags.value["checkoutExperiment"];
const variant = experimentFlag?.conditions?.variant || "control";

switch (variant) {
case "variantA":
return CheckoutV2;
case "variantB":
return CheckoutV3;
default:
return CheckoutV1;
}
});

const handlePurchase = (data: any) => {
// Track A/B test results
console.log("Purchase completed with variant:", checkoutComponent.value.name);
};
</script>

Conditional Component Registration

// plugins/conditionalComponents.ts
import type { App } from "vue";
import type { FeatureFlagService } from "@flagpole/client-vue";

export function registerConditionalComponents(
app: App,
service: FeatureFlagService
) {
// Register components based on feature flags
if (service.isFeatureEnabled("advancedCharts")) {
app.component(
"AdvancedChart",
() => import("./components/AdvancedChart.vue")
);
}

if (service.isFeatureEnabled("betaWidgets")) {
app.component("BetaWidget", () => import("./components/BetaWidget.vue"));
}
}

Reactive Theme Switching

<template>
<div :class="themeClasses">
<h1>Dynamic Theming</h1>
<p>Theme changes based on feature flags</p>
</div>
</template>

<script setup lang="ts">
import { computed } from "vue";
import { useFeatureFlag } from "@flagpole/client-vue";

const isDarkTheme = useFeatureFlag("darkTheme");
const isPremiumTheme = useFeatureFlag("premiumTheme");
const isHighContrast = useFeatureFlag("highContrast");

const themeClasses = computed(() => ({
"dark-theme": isDarkTheme.value,
"premium-theme": isPremiumTheme.value,
"high-contrast": isHighContrast.value,
}));
</script>

<style scoped>
.dark-theme {
background: #1a1a1a;
color: #ffffff;
}

.premium-theme {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}

.high-contrast {
filter: contrast(1.5);
}
</style>

Best Practices

API Key Security

Store API keys in environment variables:

// main.ts
app.use(
createFlagpole({
apiKey: import.meta.env.VITE_FLAGPOLE_API_KEY,
environments: import.meta.env.VITE_ENVIRONMENT
? [import.meta.env.VITE_ENVIRONMENT]
: undefined,
})
);

Error Handling

Implement comprehensive error handling: