diff --git a/apps/nextjs/src/app/layout.tsx b/apps/nextjs/src/app/layout.tsx
index a81cfc132..d476bfad8 100644
--- a/apps/nextjs/src/app/layout.tsx
+++ b/apps/nextjs/src/app/layout.tsx
@@ -20,6 +20,7 @@ import "@/app/theme-config.css";
import { Providers } from "@/components/AppComponents/Chat//providers";
import { AnalyticsProvider } from "@/components/ContextProviders/AnalyticsProvider";
import { CookieConsentProvider } from "@/components/ContextProviders/CookieConsentProvider";
+import { FeatureFlagProvider } from "@/components/ContextProviders/FeatureFlagProvider";
import FontProvider from "@/components/ContextProviders/FontProvider";
import { GleapProvider } from "@/components/ContextProviders/GleapProvider";
import { WebDebuggerPosition } from "@/lib/avo/Avo";
@@ -118,7 +119,13 @@ export default async function RootLayout({
}}
bootstrappedFeatures={bootstrappedFeatures}
>
- {children}
+
+
+ {children}
+
+
diff --git a/apps/nextjs/src/components/ContextProviders/FeatureFlagProvider.tsx b/apps/nextjs/src/components/ContextProviders/FeatureFlagProvider.tsx
new file mode 100644
index 000000000..9ad4cc76d
--- /dev/null
+++ b/apps/nextjs/src/components/ContextProviders/FeatureFlagProvider.tsx
@@ -0,0 +1,82 @@
+"use client";
+
+import type { ReactNode } from "react";
+import {
+ useMemo,
+ createContext,
+ useContext,
+ useEffect,
+ useState,
+ useRef,
+} from "react";
+
+import { aiLogger } from "@oakai/logger";
+
+import useAnalytics from "@/lib/analytics/useAnalytics";
+
+const log = aiLogger("feature-flags");
+
+export interface FeatureFlagContextProps {
+ bootstrappedFeatures: Record;
+}
+
+const FeatureFlagContext = createContext({
+ bootstrappedFeatures: {},
+});
+
+export interface FeatureFlagProviderProps {
+ children: ReactNode;
+ bootstrappedFeatures: Record;
+}
+
+export const FeatureFlagProvider = ({
+ children,
+ bootstrappedFeatures,
+}: FeatureFlagProviderProps) => {
+ const value = useMemo(
+ () => ({ bootstrappedFeatures }),
+ [bootstrappedFeatures],
+ );
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useClientSideFeatureFlag = (flag: string) => {
+ const context = useContext(FeatureFlagContext);
+
+ const { posthogAiBetaClient: posthog } = useAnalytics();
+ const hasLogged = useRef(false);
+
+ const [posthogFeatureFlag, setPosthogFeatureFlag] = useState<
+ boolean | string | undefined
+ >();
+
+ const bootstrappedFlag = context.bootstrappedFeatures[flag];
+
+ useEffect(() => {
+ return posthog.onFeatureFlags(() => {
+ const updatedValue = posthog.isFeatureEnabled(flag);
+ if (updatedValue !== bootstrappedFlag) {
+ log.info(`Updating ${flag} to ${updatedValue}`);
+ setPosthogFeatureFlag(updatedValue);
+ }
+ });
+ }, [posthog, flag, bootstrappedFlag]);
+
+ const isDebug = process.env.NEXT_PUBLIC_POSTHOG_DEBUG === "true";
+ if (isDebug) {
+ if (!hasLogged.current) {
+ hasLogged.current = true;
+ log.info(`Feature flag ${flag} is enabled in debug mode`);
+ }
+ return true;
+ }
+
+ // NOTE: This will flash from the bootstrapped value to the posthog value
+ // only on page load within 1 minute of toggling a flag
+ return posthogFeatureFlag ?? bootstrappedFlag ?? false;
+};
diff --git a/apps/nextjs/src/utils/useClientSideFeatureFlag.ts b/apps/nextjs/src/utils/useClientSideFeatureFlag.ts
deleted file mode 100644
index fb07ab398..000000000
--- a/apps/nextjs/src/utils/useClientSideFeatureFlag.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { useEffect, useState } from "react";
-
-import { aiLogger } from "@oakai/logger";
-
-import useAnalytics from "@/lib/analytics/useAnalytics";
-
-const log = aiLogger("feature-flags");
-
-export function useClientSideFeatureFlag(flag: string): boolean {
- const { posthogAiBetaClient: client } = useAnalytics();
-
- const [featureEnabled, setFeatureEnabled] = useState();
-
- useEffect(() => {
- const isDebug = process.env.NEXT_PUBLIC_POSTHOG_DEBUG === "true";
-
- if (isDebug) {
- log.info(`Feature flag ${flag} is enabled in debug mode`);
- setFeatureEnabled(true);
- } else {
- return client.onFeatureFlags(() => {
- setFeatureEnabled(client.isFeatureEnabled(flag));
- });
- }
- }, [client, flag]);
-
- return featureEnabled ?? false;
-}