Important
This is an experimental project to demonstrate the use of LaunchDarkly with Next.js App Router.
This is designed for the App Router. Pages router is not supported.
This solution uses the Node Server SDK and the Javascript SDK. It features:
- Server side rendering with both Server Components and Client Components.
- A client example located in
/app/components/helloLDClient.tsx
- A React Server Component (RSC) example in
/app/components/helloLDRSC.tsx
- A universal variation method for calling features on both client and server.
This is a Next.js project bootstrapped with create-next-app
using App Router.
To run this project:
- Update the .env.local file with your LaunchDarkly SDK Key configurations.
LD_SDK_KEY='<YOUR LD SERVER SDK KEY>'
NEXT_PUBLIC_LD_CLIENT_SIDE_ID='<YOUR LD CLIENT SDK KEY>'
Optional -
- Either create
dev-test-flag
in your LaunchDarkly environment or replace with your own flags inhelloLDClient.tsx
and/orhelloLDRSC.tsx
. yarn && yarn dev
ornpm i && npm run dev
You should see your flag value rendered in the browser.
The code under ld
exposes server and client apis.
-
initNodeSdk
- Initializes the Node SDK on server startup using the instrumentation hook -
getBootstrap
- Returns a json suitable for bootstrapping the js sdk. -
useLDClientRsc
- Use this to get an ldClient for Server Components.
-
LDProvider
- The react context provider. -
useLDClient
- Use this to get an ldClient for Client Components.
Follow these instructions if you want to test this apis in your own project:
- Enable instrumentationHook in
next.config.mjs
:
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: { instrumentationHook: true },
};
export default nextConfig;
- Create a new file
instrumentation.ts
at the root of your project. This will initialize the Node Server SDK.
import { initNodeSdk } from '@/ld/server';
export async function register() {
await initNodeSdk();
}
- In your root layout component, render the
LDProvider
using yourLDContext
andbootstrap
:
export default async function RootLayout({
children,
}: Readonly<{
children: ReactNode;
}>) {
// You must supply an LDContext. For example, here getLDContext
// inspects cookies and defaults to anonymous.
const context = getLDContext();
// A bootstrap is required to initialize LDProvider.
const bootstrap = await getBootstrap(context);
return (
<html lang="en">
<body className={inter.className}>
<LDProvider context={context} options={{ bootstrap }}>
{children}
</LDProvider>
</body>
</html>
);
}
- Server Components must use the
useLDClientRsc
function, and can be async or non-async:
// You should use your own getLDContext function.
import { getLDContext } from '@/app/utils';
import { useLDClientRsc } from '@/ld/server';
export default async function HelloRSC() {
const ldc = await useLDClientRsc(getLDContext());
const flagValue = ldc.variation('dev-test-flag');
return (
<div className="border-2 border-white/20 p-4">
<p className="text-xl ldgradient">
{flagValue
? "This flag is evaluating True in a React Server Component"
: "This flag is evaluating False in a React Server Component"}
</p>
</div>
);
}
- Client Components must use the
useLDClient
hook:
'use client';
import { useLDClient } from '@/ld/client';
export default function HelloClient() {
const ldc = useLDClient();
const flagValue = ldc.variation('dev-test-flag');
return (
<div className="border-2 border-white/20 p-4 ">
<p className="ldgradient text-xl">
{flagValue
? "This flag is evaluating True running Client-Side JavaScript"
: "This flag is evaluating False running Client-Side JavaScript"}
</p>
</div>
);
}
You will see both components are rendered on the server (view source on your browser). However, only Client Components will respond to live changes.