- Vercel
- Edge Config
- Statsig
- LaunchDarkly
- PostHog
- GrowthBook
- Hypertune
- Flagsmith
- Reflag
- OpenFeature
- Split
- Optimizely
- Custom Adapters
Package: @flags-sdk/vercel
pnpm i flags @flags-sdk/vercelPrerequisite: The
vercel flagscommands require the Vercel CLI to be installed and authenticated. If the Vercel CLI is not installed, runpnpm i -g vercel. For authentication or linking issues, read and follow thevercel-cliskill. If it's not installed, runnpx skills add https://github.com/vercel/vercel --skill vercel-cli.
Before running any vercel flags command, verify the project is linked to Vercel. Check for a .vercel directory in the project root. If it doesn't exist, run vercel link first.
- Create a flag in the Vercel dashboard or via CLI:
vercel flags add <flag-key> --kind boolean --description "<description>" - Pull env vars: you must run
vercel env pullto writeFLAGSandFLAGS_SECRETto.env.local. Without these environment variables,vercelAdapter()will not be able to evaluate flags. - Declare the flag:
import { flag } from 'flags/next';
import { vercelAdapter } from '@flags-sdk/vercel';
export const exampleFlag = flag({
key: 'example-flag',
adapter: vercelAdapter(),
});import { dedupe, flag } from 'flags/next';
import { vercelAdapter } from '@flags-sdk/vercel';
type Entities = {
team?: { id: string };
user?: { id: string };
};
const identify = dedupe(async (): Promise<Entities> => ({
team: { id: 'team-123' },
user: { id: 'user-456' },
}));
export const exampleFlag = flag<boolean, Entities>({
key: 'example-flag',
identify,
adapter: vercelAdapter(),
});import { createFlagsDiscoveryEndpoint } from 'flags/next';
import { getProviderData } from '@flags-sdk/vercel';
import * as flags from '../../../../flags';
export const GET = createFlagsDiscoveryEndpoint(async () => {
return await getProviderData(flags);
});import { createVercelAdapter } from '@flags-sdk/vercel';
const customAdapter = createVercelAdapter(process.env.CUSTOM_FLAGS_KEY!);
export const exampleFlag = flag({
key: 'example-flag',
adapter: customAdapter(),
});If the app also uses @vercel/flags-core directly, create the client once and pass it to the adapter so both share the same instance:
import { createClient } from '@vercel/flags-core';
import { createVercelAdapter } from '@flags-sdk/vercel';
const vercelFlagsClient = createClient(process.env.FLAGS);
const vercelAdapter = createVercelAdapter(vercelFlagsClient);
export const exampleFlag = flag({
key: 'example-flag',
adapter: vercelAdapter(),
});Manage Vercel Flags from the terminal. Requires the Vercel CLI and a linked project.
Prerequisite: The Vercel CLI must be installed (
pnpm i -g vercel) and the project must be linked (vercel link— check for a.verceldirectory). For authentication issues, read and follow thevercel-cliskill.
| Subcommand | Description |
|---|---|
list |
List all flags in the project |
add |
Create a new flag |
inspect |
Show details, status, and targeting rules of a flag |
enable |
Enable a boolean flag for a specific environment |
disable |
Disable a boolean flag for a specific environment |
archive |
Archive a flag (required before deleting) |
rm |
Delete an archived flag |
sdk-keys |
Manage SDK keys (subcommands: ls, add, rm) |
# Create a boolean flag with a description
vercel flags add my-feature --kind boolean --description "New onboarding flow"
# Enable in development first
vercel flags enable my-feature --environment development
# Promote to production
vercel flags enable my-feature --environment production
# Disable in production
vercel flags disable my-feature --environment production
# Change string variant in production
vercel flags set my-feature -e production --variant my-variantenable and disable only work with boolean flags. For changing the state of other flag types, use the set command. Use the vercel-cli skill for full reference.
# Show details of a specific flag (status, environments, targeting rules)
vercel flags inspect my-feature
# List all flags in the project
vercel flags listA flag must be archived before it can be deleted:
vercel flags archive my-feature
vercel flags rm my-featureSDK keys connect your application to Vercel Flags. The FLAGS environment variable contains an SDK key.
# List SDK keys for the project
vercel flags sdk-keys ls
# Create a new SDK key
vercel flags sdk-keys add
# Remove an SDK key
vercel flags sdk-keys rm <sdk-key-id>These examples cover common flag operations. For the full vercel flags reference and other Vercel CLI commands, see the vercel-cli skill. If it's not installed: npx skills add https://github.com/vercel/vercel --skill vercel-cli
Full CLI reference: https://vercel.com/docs/cli/flags
Package: @flags-sdk/edge-config
pnpm i @flags-sdk/edge-configEnv: EDGE_CONFIG="edge-config-connection-string"
import { flag } from 'flags/next';
import { edgeConfigAdapter } from '@flags-sdk/edge-config';
export const exampleFlag = flag({
adapter: edgeConfigAdapter(),
key: 'example-flag',
});Edge Config should contain:
{
"flags": {
"example-flag": true,
"another-flag": false
}
}import { createEdgeConfigAdapter } from '@flags-sdk/edge-config';
const myAdapter = createEdgeConfigAdapter({
connectionString: process.env.OTHER_EDGE_CONFIG,
options: {
edgeConfigItemKey: 'other-flags-key',
teamSlug: 'my-team',
},
});Package: @flags-sdk/statsig
pnpm i @flags-sdk/statsigEnv vars:
STATSIG_SERVER_API_KEY(required)STATSIG_PROJECT_ID(optional)EXPERIMENTATION_CONFIG(optional, Edge Config)EXPERIMENTATION_CONFIG_ITEM_KEY(optional)
import { statsigAdapter, type StatsigUser } from '@flags-sdk/statsig';
// Feature Gates
export const myGate = flag<boolean, StatsigUser>({
key: 'my_feature_gate',
adapter: statsigAdapter.featureGate((gate) => gate.value),
identify,
});
// Dynamic Configs
export const myConfig = flag<Record<string, unknown>, StatsigUser>({
key: 'my_dynamic_config',
adapter: statsigAdapter.dynamicConfig((config) => config.value),
identify,
});
// Experiments
export const myExperiment = flag<Record<string, unknown>, StatsigUser>({
key: 'my_experiment',
adapter: statsigAdapter.experiment((config) => config.value),
identify,
});
// Autotune
export const myAutotune = flag<Record<string, unknown>, StatsigUser>({
key: 'my_autotune',
adapter: statsigAdapter.autotune((config) => config.value),
identify,
});
// Layers
export const myLayer = flag<Record<string, unknown>, StatsigUser>({
key: 'my_layer',
adapter: statsigAdapter.layer((layer) => layer.value),
identify,
});Use . to distinguish flags from the same config:
export const text = flag<string, StatsigUser>({
key: 'my_config.text',
adapter: statsigAdapter.dynamicConfig((c) => c.value.text as string),
identify,
});
export const price = flag<number, StatsigUser>({
key: 'my_config.price',
adapter: statsigAdapter.dynamicConfig((c) => c.value.price as number),
identify,
});Disabled by default (middleware prefetch would cause premature exposures). Enable explicitly:
adapter: statsigAdapter.featureGate((gate) => gate.value, {
exposureLogging: true,
})Log exposures from the client instead when possible.
import { getProviderData as getStatsigProviderData } from '@flags-sdk/statsig';
import { mergeProviderData } from 'flags';
export const GET = createFlagsDiscoveryEndpoint(async () => {
return mergeProviderData([
getProviderData(flags),
getStatsigProviderData({
consoleApiKey: process.env.STATSIG_CONSOLE_API_KEY,
projectId: process.env.STATSIG_PROJECT_ID,
}),
]);
});Package: @flags-sdk/launchdarkly
pnpm i @flags-sdk/launchdarklyEnv vars:
LAUNCHDARKLY_CLIENT_SIDE_ID(required)LAUNCHDARKLY_PROJECT_SLUG(required)EDGE_CONFIG(required)
import { ldAdapter, type LDContext } from '@flags-sdk/launchdarkly';
const identify = dedupe((async ({ headers, cookies }) => {
const user = await getUser(headers, cookies);
return { key: user.userID };
}) satisfies Identify<LDContext>);
export const exampleFlag = flag<boolean, LDContext>({
key: 'example-flag',
identify,
adapter: ldAdapter.variation(),
});import { getProviderData as getLDProviderData } from '@flags-sdk/launchdarkly';
return mergeProviderData([
getProviderData(flags),
getLDProviderData({
apiKey: process.env.LAUNCHDARKLY_API_KEY,
projectKey: process.env.LAUNCHDARKLY_PROJECT_KEY,
environment: process.env.LAUNCHDARKLY_ENVIRONMENT,
}),
]);Package: @flags-sdk/posthog
pnpm i @flags-sdk/posthogEnv vars:
NEXT_PUBLIC_POSTHOG_KEYNEXT_PUBLIC_POSTHOG_HOST(e.g.https://us.i.posthog.com)
import { postHogAdapter } from '@flags-sdk/posthog';
// Boolean check
export const myFlag = flag({
key: 'my-flag',
adapter: postHogAdapter.isFeatureEnabled(),
identify,
});
// Multivariate value
export const myVariant = flag({
key: 'my-flag',
adapter: postHogAdapter.featureFlagValue(),
identify,
});
// Payload
export const myPayload = flag({
key: 'my-flag',
adapter: postHogAdapter.featureFlagPayload((v) => v),
defaultValue: {},
identify,
});Requires: POSTHOG_PERSONAL_API_KEY, POSTHOG_PROJECT_ID
import { getProviderData as getPostHogProviderData } from '@flags-sdk/posthog';
export const GET = createFlagsDiscoveryEndpoint(() =>
getPostHogProviderData({
personalApiKey: process.env.POSTHOG_PERSONAL_API_KEY,
projectId: process.env.NEXT_PUBLIC_POSTHOG_PROJECT_ID,
}),
);Package: @flags-sdk/growthbook
pnpm i @flags-sdk/growthbookEnv: GROWTHBOOK_CLIENT_KEY (required)
import { growthbookAdapter, type Attributes } from '@flags-sdk/growthbook';
const identify = dedupe((async ({ cookies }) => ({
id: cookies.get('user_id')?.value,
})) satisfies Identify<Attributes>);
export const myFlag = flag({
key: 'my_feature',
identify,
adapter: growthbookAdapter.feature<boolean>(),
});Set GROWTHBOOK_EDGE_CONNECTION_STRING or EXPERIMENTATION_CONFIG (Vercel Marketplace).
growthbookAdapter.setTrackingCallback((experiment, result) => {
after(async () => {
console.log('Experiment', experiment.key, 'Variation', result.key);
});
});Package: @flags-sdk/hypertune
pnpm i hypertune flags server-only @flags-sdk/hypertune @vercel/edge-configRequires code generation: npx hypertune
import { createHypertuneAdapter } from '@flags-sdk/hypertune';
import { createSource, flagFallbacks, vercelFlagDefinitions, type Context, type FlagValues } from './generated/hypertune';
const hypertuneAdapter = createHypertuneAdapter<FlagValues, Context>({
createSource,
flagFallbacks,
flagDefinitions: vercelFlagDefinitions,
identify,
});
export const exampleFlag = flag(hypertuneAdapter.declarations.exampleFlag);Package: @flags-sdk/flagsmith
pnpm i @flags-sdk/flagsmithEnv: FLAGSMITH_ENVIRONMENT_ID (required)
import { flagsmithAdapter } from '@flags-sdk/flagsmith';
export const buttonColor = flag<string>({
key: 'button-color',
defaultValue: 'blue',
adapter: flagsmithAdapter.getValue({ coerce: 'string' }),
});
export const showBanner = flag<boolean>({
key: 'show-banner',
defaultValue: false,
adapter: flagsmithAdapter.getValue({ coerce: 'boolean' }),
});Coercion options: 'string', 'number', 'boolean', or omit for raw value.
Package: @flags-sdk/reflag
pnpm i @flags-sdk/reflagEnv: REFLAG_SECRET_KEY
import { reflagAdapter, type Context } from '@flags-sdk/reflag';
const identify = dedupe((async ({ headers, cookies }) => ({
user: { id: 'user-id', name: 'name', email: 'email' },
company: { id: 'company-id' },
})) satisfies Identify<Context>);
export const myFeature = flag<boolean, Context>({
key: 'my_feature',
identify,
adapter: reflagAdapter.isEnabled(),
});Package: @flags-sdk/openfeature + @openfeature/server-sdk
pnpm i @flags-sdk/openfeature @openfeature/server-sdkimport { createOpenFeatureAdapter } from '@flags-sdk/openfeature';
// Sync provider
OpenFeature.setProvider(new YourProvider());
const adapter = createOpenFeatureAdapter(OpenFeature.getClient());
// Async provider
const adapter = createOpenFeatureAdapter(async () => {
await OpenFeature.setProviderAndWait(new YourProvider());
return OpenFeature.getClient();
});adapter.booleanValue() // boolean flags
adapter.stringValue() // string flags
adapter.numberValue() // number flags
adapter.objectValue() // object flagsAll require defaultValue on the flag declaration.
Package: @flags-sdk/split (Flags Explorer only, adapter coming soon)
import { getProviderData as getSplitProviderData } from '@flags-sdk/split';
getSplitProviderData({
adminApiKey: process.env.SPLIT_ADMIN_API_KEY,
environmentId: process.env.SPLIT_ENVIRONMENT_ID,
organizationId: process.env.SPLIT_ORG_ID,
workspaceId: process.env.SPLIT_WORKSPACE_ID,
});Package: @flags-sdk/optimizely (Flags Explorer only, adapter coming soon)
import { getProviderData as getOptimizelyProviderData } from '@flags-sdk/optimizely';
getOptimizelyProviderData({
projectId: process.env.OPTIMIZELY_PROJECT_ID,
apiKey: process.env.OPTIMIZELY_API_KEY,
});Create an adapter factory:
import type { Adapter } from 'flags';
export function createMyAdapter(/* options */) {
return function myAdapter<ValueType, EntitiesType>(): Adapter<ValueType, EntitiesType> {
return {
origin(key) {
return `https://my-provider.com/flags/${key}`;
},
async decide({ key }): Promise<ValueType> {
return false as ValueType;
},
};
};
}Expose a lazily-initialized default for simpler usage:
let defaultAdapter: ReturnType<typeof createMyAdapter> | undefined;
export function myAdapter<V, E>(): Adapter<V, E> {
if (!defaultAdapter) {
if (!process.env.MY_API_KEY) throw new Error('Missing MY_API_KEY');
defaultAdapter = createMyAdapter(process.env.MY_API_KEY);
}
return defaultAdapter<V, E>();
}Usage:
import { myAdapter } from './my-adapter';
export const exampleFlag = flag({
key: 'example',
adapter: myAdapter(),
});