Next.js
Introduction
The Next.js SDK combines a client and a server SDK to provide configs to the browser, to server routes, and to SSR. It is intended to be used by Next.js web applications running on modern web browsers. All modern web browsers on popular platforms should be supported.
The minimum supported version of Next.js is 14.0.
Browser SDKs are tested on the latest versions of Chrome, Firefox, Safari, and Edge.
Telemetry collection within the browser uses Web Workers to aggregate and send telemetry data. If the SDK runs on older browsers without Web Workers support, config evaluation will continue to work but no telemetry data will be collected for that session.
Installation
The SDK can be installed from NPM: https://www.npmjs.com/package/@configdirector/nextjs-sdk
npm install --save @configdirector/nextjs-sdk
yarn add @configdirector/nextjs-sdk
pnpm add @configdirector/nextjs-sdk
bun add @configdirector/nextjs-sdk
Configure the SDK
Set up the server instance to initialize at startup
The Next.js SDK maintains a singleton server SDK instance used in server routes and server side rendering (SSR). In order to register this instance at startup, you can make use of the Next.js instrumentation.ts file:
export async function register() {
const { register } = await import("@configdirector/nextjs-sdk/server");
await register({
// The server SDK key is a secret value, do not commit it to your source code repository.
// In this example, we are assuming the environment variable CONFIGDIRECTOR_SERVER_SDK_KEY
// will be populated at runtime with your server SDK key in the server environment
serverSdkKey: process.env["CONFIGDIRECTOR_SERVER_SDK_KEY"],
});
}
If you are using a version of Next.js older than 15, instrumentation is an experimental feature that must be enabled explicitly:
module.exports = {
experimental: {
instrumentationHook: true,
},
};
Configure the client provider
The ConfigDirector hooks are only available within a ConfigDirectorProvider. The simplest approach is to wrap your application in a ConfigDirectorProvider in your layout:
import { ConfigDirectorProvider } from "@configdirector/nextjs-sdk/server";
import type { ReactNode } from "react";
export default async function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en">
<body>
<ConfigDirectorProvider sdkKey={process.env["NEXT_PUBLIC_CONFIGDIRECTOR_CLIENT_SDK_KEY"]}>
<main>{children}</main>
</ConfigDirectorProvider>
</body>
</html>
);
}
Additionally, you can configure the provider with a user context. In this example, we retrieve the userId from a cookie:
import { ConfigDirectorProvider } from "@configdirector/nextjs-sdk/server";
import type { ReactNode } from "react";
import { cookies } from "next/headers";
export default async function RootLayout({ children }: { children: ReactNode }) {
const userId = (await cookies()).get("userId")?.value;
return (
<html lang="en">
<body>
<ConfigDirectorProvider
sdkKey={process.env["NEXT_PUBLIC_CONFIGDIRECTOR_CLIENT_SDK_KEY"]}
context={{ userId }}>
<main>{children}</main>
</ConfigDirectorProvider>
</body>
</html>
);
}
Additional configuration options
appName and appVersion
These metadata options allow you to provide your application's name and version. These values can be used in targeting rules conditionals. For example, if a certain feature should only be enabled starting with a certain version of your application.
export async function register() {
const { register } = await import("@configdirector/nextjs-sdk/server");
await register({
serverSdkKey: process.env["CONFIGDIRECTOR_SERVER_SDK_KEY"],
metadata: {
appName: "YOUR-APP-NAME",
appVersion: "1.0.2",
},
});
}
The appName and appVersion are automatically propagated to the client SDK instance via the ConfigDirectorProvider RSC. However, if needed they can be overridden to a different value when configuring the provider as well:
import { ConfigDirectorProvider } from "@configdirector/nextjs-sdk/server";
import type { ReactNode } from "react";
export default async function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en">
<body>
<ConfigDirectorProvider
sdkKey={process.env["NEXT_PUBLIC_CONFIGDIRECTOR_CLIENT_SDK_KEY"]}
appName="A-DIFFERENT-APP-NAME"
appVersion="1.0.3">
<main>{children}</main>
</ConfigDirectorProvider>
</body>
</html>
);
}
logger (server only)
By default, the SDK on the server side logs to the console and it is set to log warnings and errors only. You can configure a logger by either creating a ConfigDirector console logger with a different log level, or by implementing the ConfigDirectorLogger interface to provide your own logger. The interface can be used to create an adapter to another logging library.
Configure the ConfigDirector console logger to a different level:
export async function register() {
const { register } = await import("@configdirector/nextjs-sdk/server");
await register({
serverSdkKey: process.env["CONFIGDIRECTOR_SERVER_SDK_KEY"],
logger: createConsoleLogger("info"),
});
}
Implement your own logger adapter:
export async function register() {
const { register, ConfigDirectorLogger } = await import("@configdirector/nextjs-sdk/server");
const myLogger: ConfigDirectorLogger = {
debug: function (message: string, ...args: any): void {
// your specific logging library implementation here
},
info: function (message: string, ...args: any): void {
// your specific logging library implementation here
},
warn: function (message: string, ...args: any): void {
// your specific logging library implementation here
},
error: function (message: string, ...args: any): void {
// your specific logging library implementation here
},
};
await register({
serverSdkKey: process.env["CONFIGDIRECTOR_SERVER_SDK_KEY"],
logger: myLogger,
});
}
logLevel (client only)
The SDK on the client side logs to the browser console. You can adjust the ConfigDirector's SDK logging level for the client side via the logLevel option (defaults to warn):
import { ConfigDirectorProvider } from "@configdirector/nextjs-sdk/server";
import type { ReactNode } from "react";
export default async function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en">
<body>
<ConfigDirectorProvider
sdkKey={process.env["NEXT_PUBLIC_CONFIGDIRECTOR_CLIENT_SDK_KEY"]}
logLevel="info">
<main>{children}</main>
</ConfigDirectorProvider>
</body>
</html>
);
}
Additional server configuration
The register function accepts the same configuration values as the Node.js server SDK. You can refer to the Node.js SDK for a full list of those configuration options.
Retrieve config values
In your client components, retrieve a config value with the useConfigValue hook. On the server side, getConfigClient returns the global server instance of the client (an instance of the Node.js SDK).
Retrieving config values in client components:
"use client";
import { useConfigValue } from "@configdirector/nextjs-sdk/client";
function YourComponent() {
const { value } = useConfigValue("my-config-key", false);
return (<p>my-config-key: {value}</p>);
}
export default YourComponent;
Retrieving config values in server routes:
import { getConfigClient } from "@configdirector/nextjs-sdk/server";
export async function GET() {
const client = getConfigClient();
const value = client.getValue("my-config-key", false);
return Response.json({ "my-config-key": value });
}
SSR config evaluation
Evaluation of config values during server side rendering (SSR) utilizes the global server instance of the ConfigDirector Node.js client. That means there are no additional network requests that take place since the server SDK evaluates targeting rules locally for any given user context.
Update the user context
The user context can be updated with the useContext hook:
"use client";
import { useConfigValue, useContext } from "@configdirector/nextjs-sdk/client";
function YourComponent() {
const { value } = useConfigValue("my-config-key", false);
const { updateContext } = useContext();
useEffect(() => {
updateContext({
id: "654321",
name: "Another User",
traits: {
region: "Australia",
},
});
}, []);
return <p>my-config-key: {value}</p>;
}
export default YourComponent;
On server routes, the user context is provided at evaluation time as the third argument to getValue. Unlike client SDKs, the server SDKs are able to evaluate targeting rules for the given user context locally without additional network calls.
import { getConfigClient } from "@configdirector/nextjs-sdk/server";
export async function GET() {
const client = getConfigClient();
const value = client.getValue("my-config-key", false, {
id: "654321",
name: "Another User",
traits: {
region: "Australia",
},
});
return Response.json({ "my-config-key": value });
}