diff --git a/next-js/src/app/client/ClientComponent.tsx b/next-js/src/app/client/ClientComponent.tsx new file mode 100644 index 0000000..6f7da0b --- /dev/null +++ b/next-js/src/app/client/ClientComponent.tsx @@ -0,0 +1,24 @@ +"use client"; +import { useFeatureIsOn, useFeatureValue } from "@growthbook/growthbook-react"; +export default function ClientComponent() { + const feature1Enabled = useFeatureIsOn("feature1"); + const feature2Value = useFeatureValue("feature2", "fallback"); + return ( +
+
Client Component
+

+ This component renders client-side. The page initially delivered to the + client will not have FF values loaded, which can result in a + 'flicker' when values are loaded asynchronously client-side. +

+ +
+ ); +} diff --git a/next-js/src/app/client/page.tsx b/next-js/src/app/client/page.tsx index cab5d88..a17f4d8 100644 --- a/next-js/src/app/client/page.tsx +++ b/next-js/src/app/client/page.tsx @@ -1,54 +1,28 @@ "use client"; -import { useEffect, useState } from "react"; +import { useEffect } from "react"; +import { GrowthBookProvider } from "@growthbook/growthbook-react"; import Cookies from "js-cookie"; -import gb from "@/lib/growthbook"; +import gb from "@/lib/growthbook/client"; +import ClientComponent from "./ClientComponent"; export default function ClientPage() { - const [isLoading, setIsLoading] = useState(false); - useEffect(() => { const load = async () => { - await gb.loadFeatures({ timeout: 1000 }); - - const userId = Cookies.get("gb-next-example-userId"); - - gb.setAttributes({ - id: userId, - }); + try { + await gb.loadFeatures(); + const userId = Cookies.get("gb-next-example-userId"); + gb.setAttributes({ + id: userId, + }); + } catch (e) { + console.error(e); + } }; - try { - setIsLoading(true); - load(); - } catch (e) { - console.error(e); - } finally { - setIsLoading(false); - } + load(); }, []); - - const feature1Enabled = gb.isOn("feature1"); - const feature2Value = gb.getFeatureValue("feature2", "fallback"); - return ( -
-
Client Component
-

- This component renders client-side. The page initially delivered to the - client will not have FF values loaded, which can result in a 'flicker' - when values are loaded asynchronously client-side. -

- {isLoading ? ( -
Loading...
- ) : ( - - )} -
+ + + ); } diff --git a/next-js/src/app/server/combo/ClientComponent.tsx b/next-js/src/app/server/combo/ClientComponent.tsx index e727fcc..1340f6f 100644 --- a/next-js/src/app/server/combo/ClientComponent.tsx +++ b/next-js/src/app/server/combo/ClientComponent.tsx @@ -1,6 +1,6 @@ "use client"; import Cookies from "js-cookie"; -import gb from "@/lib/growthbook"; +import gb from "@/lib/growthbook/client"; import { FeatureDefinition } from "@growthbook/growthbook"; export default function ClientComponent({ diff --git a/next-js/src/app/server/combo/page.tsx b/next-js/src/app/server/combo/page.tsx index 068b570..2428334 100644 --- a/next-js/src/app/server/combo/page.tsx +++ b/next-js/src/app/server/combo/page.tsx @@ -1,4 +1,4 @@ -import gb from "@/lib/growthbook"; +import gb from "@/lib/growthbook/server"; import { cookies } from "next/headers"; import ClientComponent from "./ClientComponent"; @@ -18,16 +18,15 @@ export default async function ServerDynamic() {
Server / Client Component Combination

- This server component fetches feature flags from GB server-side, then - passes the features object to a client-side component. The object passed - needs to be{" "} + This server component fetches feature flags from GB, then passes the + features object to a client-side component. Since the object passed is{" "} serializable - {" "} - for this to work. + + , we can avoid redundantly fetching the features object client-side.

diff --git a/next-js/src/app/server/dynamic/ClientComponent.tsx b/next-js/src/app/server/dynamic/ClientComponent.tsx index 76eb8b6..affb14d 100644 --- a/next-js/src/app/server/dynamic/ClientComponent.tsx +++ b/next-js/src/app/server/dynamic/ClientComponent.tsx @@ -1,7 +1,7 @@ "use client"; import { useEffect, useState } from "react"; import Cookies from "js-cookie"; -import gb from "@/lib/growthbook"; +import gb from "@/lib/growthbook/client"; export default function ClientPage() { const [isLoading, setIsLoading] = useState(false); diff --git a/next-js/src/app/server/dynamic/page.tsx b/next-js/src/app/server/dynamic/page.tsx index 4a10802..f027cfe 100644 --- a/next-js/src/app/server/dynamic/page.tsx +++ b/next-js/src/app/server/dynamic/page.tsx @@ -1,4 +1,4 @@ -import gb from "@/lib/growthbook"; +import gb from "@/lib/growthbook/server"; import { cookies } from "next/headers"; export default async function ServerDynamic() { diff --git a/next-js/src/app/server/static/page.tsx b/next-js/src/app/server/static/page.tsx index 8c44bf5..67282f7 100644 --- a/next-js/src/app/server/static/page.tsx +++ b/next-js/src/app/server/static/page.tsx @@ -1,4 +1,4 @@ -import gb from "@/lib/growthbook"; +import gb from "@/lib/growthbook/server"; export default async function ServerStatic() { await gb.loadFeatures({ timeout: 1000 }); gb.setAttributes({ diff --git a/next-js/src/app/server/streaming/AsyncComponent.tsx b/next-js/src/app/server/streaming/AsyncComponent.tsx index 2a058e6..323e717 100644 --- a/next-js/src/app/server/streaming/AsyncComponent.tsx +++ b/next-js/src/app/server/streaming/AsyncComponent.tsx @@ -1,4 +1,4 @@ -import gb from "@/lib/growthbook"; +import gb from "@/lib/growthbook/server"; import { cookies } from "next/headers"; export default async function AsyncComponent() { diff --git a/next-js/src/lib/growthbook/client.ts b/next-js/src/lib/growthbook/client.ts new file mode 100644 index 0000000..a79e596 --- /dev/null +++ b/next-js/src/lib/growthbook/client.ts @@ -0,0 +1,11 @@ +import { GrowthBook } from "@growthbook/growthbook"; + +const growthbook = new GrowthBook({ + apiHost: process.env.NEXT_PUBLIC_GROWTHBOOK_API_HOST, + clientKey: process.env.NEXT_PUBLIC_GROWTHBOOK_CLIENT_KEY, + decryptionKey: process.env.NEXT_PUBLIC_GROWTHBOOK_DECRYPTION_KEY, + // client-side features enabled + subscribeToChanges: true, +}); + +export default growthbook; diff --git a/next-js/src/lib/growthbook.ts b/next-js/src/lib/growthbook/server.ts similarity index 100% rename from next-js/src/lib/growthbook.ts rename to next-js/src/lib/growthbook/server.ts diff --git a/next-js/src/middleware.ts b/next-js/src/middleware.ts index 4f939e7..6a62b06 100644 --- a/next-js/src/middleware.ts +++ b/next-js/src/middleware.ts @@ -1,7 +1,7 @@ import { NextResponse } from "next/server"; import type { NextRequest } from "next/server"; import { cookies } from "next/headers"; -import gb from "@/lib/growthbook"; +import gb from "@/lib/growthbook/server"; export async function middleware(request: NextRequest) { await gb.loadFeatures({ timeout: 1000 });