Skip to content

Vercel Flags SDK

Vercel's Flags SDK is the flags-as-code layer for Next.js and SvelteKit. Its OpenFeature adapter accepts any OpenFeature provider — including Lucerna's — so Gates plugs into flag() declarations without a native adapter.

Setup

bash
pnpm add flags @flags-sdk/openfeature @lucerna-dev/gates-openfeature @lucerna-dev/gates-node @openfeature/server-sdk

Declare the adapter once and use it in every flag:

ts
// flags.ts
import { flag } from "flags/next";
import { createOpenFeatureAdapter } from "@flags-sdk/openfeature";
import { OpenFeature } from "@openfeature/server-sdk";
import { LucernaProvider } from "@lucerna-dev/gates-openfeature";

const lucernaAdapter = createOpenFeatureAdapter(async () => {
  await OpenFeature.setProviderAndWait(
    new LucernaProvider({ serverKey: process.env.LUCERNA_SERVER_KEY! }),
  );
  return OpenFeature.getClient();
});

export const showNewBilling = flag<boolean>({
  key: "new_billing_page",
  defaultValue: false,
  identify: ({ cookies }) => ({ targetingKey: cookies.get("uid")?.value }),
  adapter: lucernaAdapter.booleanValue(),
});

Then read it in a server component or route handler:

tsx
import { showNewBilling } from "./flags";

export default async function Page() {
  const newBilling = await showNewBilling();
  return newBilling ? <NewBilling /> : <ClassicBilling />;
}

Experiments

Declare experiment flags as flag<string> — the variant name comes back through getStringValue, and the exposure is recorded server-side on the read:

ts
export const checkoutTest = flag<string>({
  key: "checkout_test",
  defaultValue: "control",
  identify: ({ cookies }) => ({ targetingKey: cookies.get("uid")?.value }),
  adapter: lucernaAdapter.stringValue(),
});

Notes

  • identify supplies the evaluation context per request; targetingKey becomes the Gates userId, so decisions stay sticky per user. See the context mapping.
  • The Flags SDK evaluates each declared flag separately. Concurrent reads for the same flag and identity share one request, and remoteCacheTtlMs can memoize across sequential reads in a warm process — see requests and caching.
  • The provider is stateless and Edge-runtime compatible: pure fetch, no timers, no Node APIs.

Lucerna Developer Docs