diff --git a/examples/example-nuxt/pages/feature-flags.vue b/examples/example-nuxt/pages/feature-flags.vue new file mode 100644 index 0000000000..996976991d --- /dev/null +++ b/examples/example-nuxt/pages/feature-flags.vue @@ -0,0 +1,77 @@ + + + diff --git a/examples/example-nuxt/pages/index.vue b/examples/example-nuxt/pages/index.vue index cce2307890..bc189bc21a 100644 --- a/examples/example-nuxt/pages/index.vue +++ b/examples/example-nuxt/pages/index.vue @@ -28,6 +28,10 @@ + +
+ → Go to Feature Flags Testing +
diff --git a/packages/nuxt/README.md b/packages/nuxt/README.md index c545d23231..10364ae3a8 100644 --- a/packages/nuxt/README.md +++ b/packages/nuxt/README.md @@ -53,6 +53,75 @@ export default defineNuxtConfig({ const { $posthog } = useNuxtApp() ``` +## Composables + +The module provides auto-imported composables for working with feature flags: + +### `usePostHog()` + +Returns the PostHog client instance. + +```vue + +``` + +### `useFeatureFlagEnabled(flag: string)` + +Returns a reactive ref that checks if a feature flag is enabled (returns `true`/`false`/`undefined`). + +```vue + + + +``` + +### `useFeatureFlagVariantKey(flag: string)` + +Returns a reactive ref containing the feature flag value/variant (returns the variant string, `true`/`false`, or `undefined`). + +```vue + + + +``` + +### `useFeatureFlagPayload(flag: string)` + +Returns a reactive ref containing the feature flag payload (returns any JSON value or `undefined`). + +```vue + + + +``` + +All these composables automatically update when feature flags are loaded or changed. + 4. On the server side, the PostHog client instance initialized by the plugin is intended exclusively for error tracking. If you require additional PostHog client functionality for other purposes, please instantiate a separate client within your application as needed. ## FAQ diff --git a/packages/nuxt/src/module.ts b/packages/nuxt/src/module.ts index 8676cba95f..5cb32d491e 100644 --- a/packages/nuxt/src/module.ts +++ b/packages/nuxt/src/module.ts @@ -1,4 +1,4 @@ -import { defineNuxtModule, addPlugin, createResolver, addServerPlugin } from '@nuxt/kit' +import { defineNuxtModule, addPlugin, createResolver, addServerPlugin, addImportsDir } from '@nuxt/kit' import type { PostHogConfig } from 'posthog-js' import type { PostHogOptions } from 'posthog-node' import { resolveBinaryPath, spawnLocal } from '@posthog/core/process' @@ -58,6 +58,7 @@ export default defineNuxtModule({ const resolver = createResolver(import.meta.url) addPlugin(resolver.resolve('./runtime/vue-plugin')) addServerPlugin(resolver.resolve('./runtime/nitro-plugin')) + addImportsDir(resolver.resolve(`./runtime/composables`)) Object.assign(nuxt.options.runtimeConfig.public, { posthog: { diff --git a/packages/nuxt/src/runtime/composables/useFeatureFlagEnabled.ts b/packages/nuxt/src/runtime/composables/useFeatureFlagEnabled.ts new file mode 100644 index 0000000000..b8d5d2821a --- /dev/null +++ b/packages/nuxt/src/runtime/composables/useFeatureFlagEnabled.ts @@ -0,0 +1,35 @@ +import { ref, onMounted, onUnmounted } from 'vue' +import { usePostHog } from './usePostHog' + +/** + * Check if a feature flag is enabled + * + * @example + * ```ts + * const isEnabled = useFeatureFlagEnabled('my-flag') + * ``` + * + * @param flag - The feature flag key + * @returns A reactive ref containing the feature flag enabled state (boolean | undefined) + */ +export function useFeatureFlagEnabled(flag: string) { + const posthog = usePostHog() + const featureEnabled = ref(posthog?.isFeatureEnabled?.(flag)) + + onMounted(() => { + if (!posthog) return + + // Update when feature flags are loaded + const unsubscribe = posthog.onFeatureFlags?.(() => { + featureEnabled.value = posthog.isFeatureEnabled(flag) + }) + + onUnmounted(() => { + if (unsubscribe) { + unsubscribe() + } + }) + }) + + return featureEnabled +} diff --git a/packages/nuxt/src/runtime/composables/useFeatureFlagPayload.ts b/packages/nuxt/src/runtime/composables/useFeatureFlagPayload.ts new file mode 100644 index 0000000000..caa6f2a02a --- /dev/null +++ b/packages/nuxt/src/runtime/composables/useFeatureFlagPayload.ts @@ -0,0 +1,36 @@ +import { ref, onMounted, onUnmounted } from 'vue' +import { usePostHog } from './usePostHog' +import type { JsonType } from 'posthog-js' + +/** + * Get the payload of a feature flag + * + * @example + * ```ts + * const payload = useFeatureFlagPayload('my-flag') + * ``` + * + * @param flag - The feature flag key + * @returns A reactive ref containing the feature flag payload + */ +export function useFeatureFlagPayload(flag: string) { + const posthog = usePostHog() + const featureFlagPayload = ref(posthog?.getFeatureFlagPayload?.(flag)) + + onMounted(() => { + if (!posthog) return + + // Update when feature flags are loaded + const unsubscribe = posthog.onFeatureFlags?.(() => { + featureFlagPayload.value = posthog.getFeatureFlagPayload(flag) + }) + + onUnmounted(() => { + if (unsubscribe) { + unsubscribe() + } + }) + }) + + return featureFlagPayload +} diff --git a/packages/nuxt/src/runtime/composables/useFeatureFlagVariantKey.ts b/packages/nuxt/src/runtime/composables/useFeatureFlagVariantKey.ts new file mode 100644 index 0000000000..d54cc8a159 --- /dev/null +++ b/packages/nuxt/src/runtime/composables/useFeatureFlagVariantKey.ts @@ -0,0 +1,35 @@ +import { ref, onMounted, onUnmounted } from 'vue' +import { usePostHog } from './usePostHog' + +/** + * Get the value/variant of a feature flag + * + * @example + * ```ts + * const variant = useFeatureFlagVariantKey('my-flag') + * ``` + * + * @param flag - The feature flag key + * @returns A reactive ref containing the feature flag value (string | boolean | undefined) + */ +export function useFeatureFlagVariantKey(flag: string) { + const posthog = usePostHog() + const featureFlagVariantKey = ref(posthog?.getFeatureFlag?.(flag)) + + onMounted(() => { + if (!posthog) return + + // Update when feature flags are loaded + const unsubscribe = posthog.onFeatureFlags?.(() => { + featureFlagVariantKey.value = posthog.getFeatureFlag(flag) + }) + + onUnmounted(() => { + if (unsubscribe) { + unsubscribe() + } + }) + }) + + return featureFlagVariantKey +} diff --git a/packages/nuxt/src/runtime/composables/usePostHog.ts b/packages/nuxt/src/runtime/composables/usePostHog.ts new file mode 100644 index 0000000000..ff9f2c3f28 --- /dev/null +++ b/packages/nuxt/src/runtime/composables/usePostHog.ts @@ -0,0 +1,18 @@ +import { useNuxtApp } from '#app' +import type posthog from 'posthog-js' + +/** + * Get the PostHog client instance + * + * @example + * ```ts + * const posthog = usePostHog() + * posthog.capture('event') + * ``` + * + * @returns The PostHog client instance + */ +export function usePostHog(): typeof posthog | undefined { + const { $posthog } = useNuxtApp() + return ($posthog as () => typeof posthog | undefined)?.() +}