---
title: Custom Adapters
description: Integrate any feature flag provider with the Flags SDK using a custom adapter.
---

# Custom Adapters



Integrate any feature flag provider with the Flags SDK
using an adapter. We publish adapters for the most [common providers](/docs/adapters/supported-providers), but
it is also possible to write a custom adapter in case we don't list
your provider or in case you have an in-house solution for feature
flags.

<CopyPrompt text="Create a custom Flags SDK adapter for my feature flag provider. Implement an adapter factory that initializes or accepts my provider client, define `origin` and `decide` behavior, optionally implement `bulkDecide` (and set `adapterId`) for batch evaluation, expose a default adapter when appropriate, use the adapter from typed flag declarations, preserve Edge Runtime compatibility where possible, and run the relevant tests or build when finished.">
  Create a custom Flags SDK adapter for my feature flag provider. Implement an
  adapter factory that initializes or accepts my provider client, define
  `origin` and `decide` behavior, optionally implement `bulkDecide` (and set
  `adapterId`) for batch evaluation, expose a default adapter when appropriate,
  use the adapter from typed flag declarations, preserve Edge Runtime
  compatibility where possible, and run the relevant tests or build when
  finished.
</CopyPrompt>

Adapters conceptually replace the `decide` and `origin` parts of a flag declaration.

## How to write a custom adapter

Creating custom adapters is possible by creating an adapter factory:

```ts title="example-adapter.ts"
import type { Adapter } from "flags";
import { createClient, GlobalConfigClient } from "@vercel/global-config";

/**
 * A factory function for your adapter
 */
export function createExampleAdapter(/* options */) {
  // create the client for your provider here, or reuse the one
  // passed in through options

  return function exampleAdapter<ValueType, EntitiesType>(): Adapter<
    ValueType,
    EntitiesType
  > {
    return {
      origin(key) {
        // link to the flag in the provider's dashboard
        return `https://example.com/flags/${key}`;
      },
      async decide({ key }): Promise<ValueType> {
        // use the SDK instance created earlier to evaluate flags here
        return false as ValueType;
      },
    };
  };
}
```

This allows passing the provider in the flag declaration.

```tsx title="flags.tsx#next"
import { flag } from "flags/next";
import { createExampleAdapter } from "./example-adapter";

// create an instance of the adapter
const exampleAdapter = createExampleAdapter();

export const exampleFlag = flag({
  key: "example-flag",
  // use the adapter for many feature flags
  adapter: exampleAdapter,
});
```

## Bulk evaluation

Adapters can implement an optional `bulkDecide` hook to share work when many flags are
evaluated together with [`evaluate`](/frameworks/next/bulk-evaluation). When `bulkDecide` is
implemented and the adapter sets an `adapterId`, `evaluate` calls `bulkDecide` once for each
group of flags that share this adapter and the same `identify` source — instead of calling
`decide` per flag. This lets the provider, for example, resolve many flags through a single
network request.

```ts title="example-adapter.ts"
return {
  // Required for `bulkDecide` to be used by `evaluate`.
  adapterId: "example",
  origin(key) {
    return `https://example.com/flags/${key}`;
  },
  async decide({ key }): Promise<ValueType> {
    return false as ValueType;
  },
  // Called by `evaluate` for a batch of flags sharing this adapter and identify.
  async bulkDecide({ flags, entities, headers, cookies }) {
    // `flags` is `{ key: string; defaultValue?: unknown }[]`.
    // Resolve them however your provider allows — ideally in a single call.
    return Object.fromEntries(
      flags.map(({ key }) => [key, false as ValueType])
    );
  },
};
```

`bulkDecide` must return a record keyed by flag key:

* Missing keys or a `value` of `undefined` fall back to that flag's `defaultValue`.
* Throwing falls back to `defaultValue` per flag (and rejects for flags without a `defaultValue`).
* A flag declared with an inline `decide` takes precedence and is excluded from bulk evaluation.

## Example

Below is an example of an Flags SDK adapter reading Global Config.

<IframeBrowser src="snippets:/concepts/adapters" codeSrc="https://github.com/vercel/flags/tree/main/examples/snippets/app/concepts/adapters" />

## Exposing default adapters

In the example above, as a user of the adapter, we first needed to
create an instance of the adapter. It is possible to simplify usage
further by exposing a default adapter.

Usage with a default adapter, where we can import a fully configured{" "}
`exampleAdapter`.

```tsx title="flags.tsx#next"
import { flag } from "flags/next";
import { exampleAdapter } from "./example-adapter";

export const exampleFlag = flag({
  key: "example-flag",
  // use the adapter for many feature flags
  adapter: exampleAdapter,
});
```

Many `@flags-sdk/*` adapters will implement this pattern. The
default adapter will get created lazily on first usage, and can
initialize itself based on known environment variables.

```ts title="example-adapter.ts"
// extend the adapter definition to expose a default adapter
let defaultGlobalConfigAdapter:
  | ReturnType<typeof createGlobalConfigAdapter>
  | undefined;

/**
 * A default Vercel adapter for Global Config
 *
 */
export function globalConfigAdapter<ValueType, EntitiesType>(): Adapter<
  ValueType,
  EntitiesType
> {
  // Initialized lazily to avoid warning when it is not actually used and env vars are missing.
  if (!defaultGlobalConfigAdapter) {
    if (!process.env.GLOBAL_CONFIG) {
      throw new Error("Global Config Adapter: Missing GLOBAL_CONFIG env var");
    }

    defaultGlobalConfigAdapter = createGlobalConfigAdapter(process.env.GLOBAL_CONFIG);
  }

  return defaultGlobalConfigAdapter<ValueType, EntitiesType>();
}
```


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Flagsmith
---

# Flagsmith



The [Flagsmith](https://flagsmith.com) provider for the [Flags SDK](https://flags-sdk.dev/) contains support for Flagsmith's Feature Flags and Remote Configuration.

<LearnMore icon="arrow" href="/providers">
  Learn more about Adapters
</LearnMore>

<LearnMore icon="arrow" href="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fexamples%2Ftree%2Fmain%2Fflags-sdk/flagsmith&env=FLAGS_SECRET&env=FLAGSMITH_ENVIRONMENT_ID&env=FLAGSMITH_PROJECT_ID&envDescription=The+FLAGS_SECRET+will+be+used+by+the+Flags+Explorer+to+securely+overwrite+feature+flags.+Must+be+32+random+bytes%2C+base64-encoded.+Use+the+generated+value+or+set+your+own.&envLink=https%3A%2F%2Fvercel.com%2Fdocs%2Fworkflow-collaboration%2Ffeature-flags%2Fsupporting-feature-flags%23flags_secret-environment-variable&project-name=flagsmith-flags-sdk-example&repository-name=flagsmith-flags-sdk-example" target="_blank">
  Deploy the template
</LearnMore>

## Setup

The Flagsmith provider is available in the `@flags-sdk/flagsmith` module. You can install it with

```bash
npm i @flags-sdk/flagsmith
```

Set the required environment variable:

```sh
export FLAGSMITH_ENVIRONMENT_ID="your-environment-id"
```

## Usage

The Flagsmith adapter provides a `getValue()` method with optional type coercion:

```ts
import { flag } from "flags/next";
import { flagsmithAdapter } from "@flags-sdk/flagsmith";

// No coercion - returns the raw value from Flagsmith
export const rawFlag = flag({
  key: "raw-value",
  defaultValue: "default",
  adapter: flagsmithAdapter.getValue(),
});

// Coerce to string type
export const buttonColor = flag<string>({
  key: "button-color",
  defaultValue: "blue",
  adapter: flagsmithAdapter.getValue({ coerce: "string" }),
});

// Coerce to number type
export const maxItems = flag<number>({
  key: "max-items",
  defaultValue: 10,
  adapter: flagsmithAdapter.getValue({ coerce: "number" }),
});

// Coerce to boolean type
export const showBanner = flag<boolean>({
  key: "show-banner",
  defaultValue: false,
  adapter: flagsmithAdapter.getValue({ coerce: "boolean" }),
});
```

### Type coercion behavior

* **Without `coerce`**: Returns the raw value from Flagsmith (empty/null/undefined values return default)
* **`coerce: "string"`**: Converts any value to string (returns default for null/undefined/NaN)
* **`coerce: "number"`**: Converts strings to numbers (returns default if result is NaN or invalid)
* **`coerce: "boolean"`**:
  * Converts `"true"`/`"false"` strings (case-insensitive) to boolean
  * Converts `0` to `false` and `1` to `true`
  * Falls back to the flag's enabled state for other values
  * Returns default when flag is disabled

## Default adapter

The default flagsmith adapter is exported as `flagsmithAdapter`.

```ts
import { flagsmithAdapter } from "@flags-sdk/flagsmith"
```

This adapter automatically configures itself based on the following environment variables:

* `FLAGSMITH_ENVIRONMENT_ID` (required): Your Flagsmith environment ID

## Custom adapter

Create a custom adapter by using the `createFlagsmithAdapter` function:

```ts
import { createFlagsmithAdapter, EntitiesType } from "@flags-sdk/flagsmith";

const identify: Identify<EntitiesType> = dedupe(async () => {
  return {
    targetingKey: "user",
    traits: {
      id: "e23cc9a8-0287-40aa-8500-6802df91e56a",
      name: "John Doe",
      email: "johndoe@flagsmith.com",
    },
  };
});

const adapter = createFlagsmithAdapter({
  environmentID: "your-environment-id",
  // Additional Flagsmith config options
});

export const showBanner = flag<boolean, EntitiesType>({
  key: "show-banner",
  identify,
  adapter: adapter.getValue({ coerce: "boolean" }),
});
```

## Flags discovery endpoint

To enable the [Flags Explorer](https://vercel.com/docs/flags/flags-explorer), create a discovery endpoint at `app/.well-known/vercel/flags/route.ts`:

```ts
import { createFlagsDiscoveryEndpoint } from "flags/next";
import { getProviderData } from "@flags-sdk/flagsmith";

export const GET = createFlagsDiscoveryEndpoint(async () => {
  return getProviderData({
    environmentKey: process.env.FLAGSMITH_ENVIRONMENT_ID,
    projectId: process.env.FLAGSMITH_PROJECT_ID,
  });
});
```

This endpoint fetches flag definitions directly from Flagsmith's API and returns them to the Flags Explorer.

## Environment variables

* `FLAGSMITH_ENVIRONMENT_ID` (required): Your Flagsmith environment ID
* `FLAGSMITH_PROJECT_ID` (optional): Required for the Flags discovery endpoint


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Global Config
---

# Global Config



The `@flags-sdk/global-config` package provides a basic adapter for defining feature flags powered by [Vercel Global Config](https://vercel.com/docs/storage/global-config)

<LearnMore icon="arrow" href="/providers">
  Learn more about Adapters
</LearnMore>

## Installation

Follow the [Quickstart](/docs/getting-started/next), then add the [Global Config adapter](https://github.com/vercel/flags/tree/main/packages/adapter-global-config).

Install desired dependencies

```sh
pnpm i @flags-sdk/global-config
```

Set relevant variables

```sh title=".env.local"
GLOBAL_CONFIG="global-config-connection-string"
```

## Usage

```ts title="flags.ts#next"
import { flag } from 'flags/next';
import { globalConfigAdapter } from '@flags-sdk/global-config';

export const exampleFlag = flag({
  // Will load the `flags` key from Global Config
  adapter: globalConfigAdapter,
  // Will get the `example-flag` key from the `flags` object
  key: 'example-flag',
});
```

Your Global Config should be [managed on the dashboard](https://vercel.com/docs/storage/global-config/global-config-dashboard) as follows:

```json
{
  // `flags` is the default used by the Global Config adapter
  "flags": {
    // Flags using the adapter should have their `key` defined here
    "example-flag": true,
    "another-example-flag": false,
  }
}
```

## API reference

### globalConfigAdapter

The adapter assumes that there is an `GLOBAL_CONFIG` environment variable set with a connection string,
and that it contains a `flags` object with each key corresponding to a flag you define in code.

```ts title="flags.ts#next"
import { globalConfigAdapter } from '@flags-sdk/global-config';

export const exampleFlag = flag({
  adapter: globalConfigAdapter,
  key: 'example-flag',
});
```

### createGlobalConfigAdapter

To customize these options you can import and call `createGlobalConfigAdapter` manually.

| Option key                    | Type     | Description                                                                                          |                                                                                                                                                                                                                                   |
| ----------------------------- | -------- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `connectionString`            | \`string | GlobalConfigClient\`                                                                                 | A [connection string](https://vercel.com/docs/storage/global-config/using-global-config#using-a-connection-string) or a [client instance](https://vercel.com/docs/storage/global-config/global-config-sdk#use-connection-strings) |
| `options.globalConfigItemKey` | `string` | Defaults to `flags`                                                                                  |                                                                                                                                                                                                                                   |
| `options.teamSlug`            | `string` | The team slug used for your team in the Vercel dashboard, used to create links to your Global Config |                                                                                                                                                                                                                                   |

```ts title="flags.ts#next"
import { createGlobalConfigAdapter } from '@flags-sdk/global-config';

const myGlobalConfigAdapter = createGlobalConfigAdapter({
  connectionString: process.env.OTHER_GLOBAL_CONFIG_CONNECTION_STRING,
  options: {
    globalConfigItemKey: 'other-flags-key',
    teamSlug: 'my-vercel-team-slug',
  },
});

export const exampleFlag = flag({
  adapter: myGlobalConfigAdapter,
  key: 'example-flag',
});
```

## Read more

Read more about Global Config, Flags SDK, and the Global Config adapter.

* [Concepts: Precompute](/principles/precompute)
* [Global Config Docs](https://vercel.com/docs/storage/global-config)
* [Global Config SDK Reference](https://vercel.com/docs/storage/global-config/global-config-sdk)
* [Global Config Examples](https://vercel.com/templates/global-config)
* [Global Config Limits and Pricing](https://vercel.com/docs/storage/global-config/global-config-limits)


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: GrowthBook
---

# GrowthBook



The GrowthBook adapter integrates [GrowthBook](https://www.growthbook.io/) with the Flags SDK, enabling feature flagging, experimentation, and configuration management in your application. This adapter lets you evaluate feature flags and experiments, with support for server-side, client-side, and Global Config bootstrapping.

GrowthBook is an open-source feature flagging and experimentation platform that helps you safely roll out features, run A/B tests, and manage configuration at scale.

<LearnMore icon="arrow" href="https://vercel.com/templates/next.js/growthbook-flags-sdk-example" target="_blank">
  Deploy the GrowthBook template
</LearnMore>

## Installation

Install the GrowthBook adapter package:

```bash
npm install @flags-sdk/growthbook
```

## Adapter usage

### Import the default adapter

A default adapter is available for use, assuming the appropriate [environment variables](#environment-variables) are set.

```ts
import { growthbookAdapter } from '@flags-sdk/growthbook';
```

### Environment variables

The default adapter considers the following environment variables:

| Environment Variable                         | Description                                                                                                        |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `GROWTHBOOK_CLIENT_KEY`                      | **Required.** GrowthBook SDK key                                                                                   |
| `GROWTHBOOK_API_HOST`                        | Optional. Override the GrowthBook API endpoint                                                                     |
| `GROWTHBOOK_APP_ORIGIN`                      | Optional. Override the GrowthBook app URL                                                                          |
| `GROWTHBOOK_GLOBAL_CONFIG_CONNECTION_STRING` | Optional. Global Config connection string                                                                          |
| `GROWTHBOOK_GLOBAL_CONFIG_ITEM_KEY`          | Optional. Global Config item key (Defaults to your client key)                                                     |
| `EXPERIMENTATION_CONFIG`                     | Optional. Used when installed through Vercel Marketplace (replaces GROWTHBOOK\_GLOBAL\_CONFIG\_CONNECTION\_STRING) |

### Create a custom adapter

You can provide custom configuration by using `createGrowthbookAdapter`:

```ts
import { createGrowthbookAdapter } from '@flags-sdk/growthbook';

const myGrowthBookAdapter = createGrowthbookAdapter({
  clientKey: process.env.GROWTHBOOK_CLIENT_KEY!,
  apiHost: process.env.GROWTHBOOK_API_HOST, // optional
  appOrigin: process.env.GROWTHBOOK_APP_ORIGIN, // optional
  globalConfig: {
    connectionString: process.env.GROWTHBOOK_GLOBAL_CONFIG_CONNECTION_STRING!,
    itemKey: process.env.GROWTHBOOK_GLOBAL_CONFIG_ITEM_KEY, // optional
  },
  trackingCallback: (experiment, result) => {
    // Custom exposure logging
  },
  clientOptions: {}, // GrowthBook ClientOptions (optional)
  initOptions: {},   // GrowthBook InitOptions (optional)
  stickyBucketService: undefined, // Optional
});
```

## User identification

GrowthBook uses [Attributes](https://docs.growthbook.io/features/targeting#attributes) to evaluate feature flags and experiments.

You should write an identify function providing these Attributes to GrowthBook flags.

```ts
import { dedupe, flag } from 'flags/next';
import type { Identify } from 'flags';
import { growthbookAdapter, type Attributes } from '@flags-sdk/growthbook';

const identify = dedupe((async ({ headers, cookies }) => {
  return {
    id: cookies.get('user_id')?.value,
    // ...other attributes
  };
}) satisfies Identify<Attributes>);

export const myFeatureFlag = flag({
  key: 'my_feature_flag',
  identify,
  adapter: growthbookAdapter.feature<boolean>(),
});
```

**Dedupe** is used above to ensure that the Attributes are computed once per request.

## Adapter methods and properties

### `feature<T>()`

This method implements the Adapter interface for a GrowthBook feature. Typically flag definitions are applied in a single file (e.g. `flags.ts`).

```ts filename="flags.ts"
export const myFlag = flag({
  key: 'my_flag',
  adapter: growthbookAdapter.feature<string>(),
  defaultValue: false,
  identify,
});
```

| Option            | Default | Description                      |
| ----------------- | ------- | -------------------------------- |
| `exposureLogging` | `true`  | Enable/disable exposure logging. |

If your flag returns a type other than `boolean`, you can provide a type argument to the `feature` method.

### `initialize`

Initializes the GrowthBook SDK. This is done on-demand when a growthbook flag is evaluated, and is not required to be called manually.

```ts
const growthbookClient = await growthbookAdapter.initialize();
```

### `setTrackingCallback`

Set a back-end callback to handle experiment exposures. This allows you to log exposures to your analytics platform. Typically this is done in the same file where your flags are defined (e.g. `flags.ts`).

```ts filename="flags.ts"
import { growthbookAdapter } from '@flags-sdk/growthbook';
import { after } from 'next/server';

growthbookAdapter.setTrackingCallback((experiment, result) => {
  // Safely fire and forget async calls (Next.js)
  after(async () => {
    console.log('Viewed Experiment', {
      experimentId: experiment.key,
      variationId: result.key,
    });
  });
});
```

Front-end experiment tracking is also supported, although it requires additional manual setup. See the [GrowthBook docs](https://docs.growthbook.io/lib/nextjs#client-side-tracking) for more information.

### `setStickyBucketService`

Sticky bucketing ensures users continue to see the same variation when you make changes to a running experiment.
GrowthBook's flavor of sticky bucketing has a few additional features:

1. Bucketing based on either a primary hash attribute (i.e. user id) or a secondary attribute (i.e. anonymous id)
2. The ability to version-control and purge your users' assigned buckets.

See GrowthBook's documentation on [Sticky Bucketing](https://docs.growthbook.io/app/sticky-bucketing#front-end-and-back-end-nodejs) for more details.

```ts
import { growthbookAdapter } from '@flags-sdk/growthbook';
import { StickyBucketService } from '@growthbook/growthbook';

class MyStickyBucketService extends StickyBucketService {
  // Implement your sticky bucket service
}

growthbookAdapter.setStickyBucketService(new MyStickyBucketService());
```

### `.growthbook`

You may access the underlying GrowthBook instance. Specifically, the GrowthBook Flags SDK adapter wraps a `GrowthBookClient` instance.

### `.stickyBucketService`

If you have set a sticky bucket service, you may retrieve its instance here.

## Global Config

The adapter can load feature configuration from [Vercel Global Config](https://vercel.com/docs/storage/global-config) to lower the latency of feature flag evaluation.

* Set `GROWTHBOOK_GLOBAL_CONFIG_CONNECTION_STRING` (or `EXPERIMENTATION_CONFIG` if installed through the Vercel Marketplace) in your environment. Optionally set `GROWTHBOOK_GLOBAL_CONFIG_ITEM_KEY` to override the default key name (defaults to your client key).
* Or pass `globalConfig` directly to the adapter.

If Global Config is not set, the adapter will fetch configuration from GrowthBook's API.

### Configuring a SDK Webhook

1. To automatically populate the Global Config whenever your feature definitions change, create a GrowthBook [SDK Webhook](https://docs.growthbook.io/app/webhooks/sdk-webhooks) on the same SDK Connection that you are using for the Next.js integration.ts

2. Select "Vercel Global Config" as the webhook type and fill out the following fields:

* Vercel Global Config ID (begins with `ecfg_`)
* Team ID (optional)
* Vercel API Token (see Vercel → Account Settings → Tokens)

Under the hood, the webhook is being configured with the following properties. If you need to change any of these settings for any reason, you can always edit the webhook.

* **Endpoint URL** is being set to
  ```
  https://api.vercel.com/v1/global-config/{global_config_id}/items
  ```
* **Method** is being set to `PATCH`
* An **Authorization: Bearer token** header is being added with your Vercel API Token
* The **Payload format** is being set to `Vercel Global Config`

## Caveats and best practices

* **Initialization:** The adapter auto-initializes when a flag is evaluated. To pre-initialize, call `initialize()` manually.
* **Exposure logging:** By default, exposures are logged when flags are evaluated. You can disable this with `exposureLogging: false` or provide a custom tracking callback.
* **Sticky Buckets:** For advanced experimentation (e.g., Bandits), use a StickyBucketService.
* **Multiple Flags, Same Key:** You can use the same GrowthBook feature key with different mapping functions if needed.

## Flags Explorer integration

To expose GrowthBook data to the [Flags Explorer](https://vercel.com/docs/flags/flags-explorer), use the `getProviderData` function in your API route:

```ts
import { getProviderData, createFlagsDiscoveryEndpoint } from 'flags/next';
import { getProviderData as getGrowthBookProviderData } from '@flags-sdk/growthbook';
import { mergeProviderData } from 'flags';
import * as flags from '../../../../flags';

export const GET = createFlagsDiscoveryEndpoint(async (request) => {
  return mergeProviderData([
    getProviderData(flags),
    getGrowthBookProviderData({
      // Add any required options here
    }),
  ]);
});
```

## More resources

* [GrowthBook Documentation](https://docs.growthbook.io/)
* [GrowthBook JS SDK Reference](https://docs.growthbook.io/lib/js)
* [Vercel Global Config](https://vercel.com/docs/storage/global-config)
* [Flags Explorer](https://vercel.com/docs/flags/flags-explorer)
* [Deploy the GrowthBook template](https://vercel.com/templates/next.js/growthbook-flags-sdk-example)


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Hypertune
---

# Hypertune



The `@flags-sdk/hypertune` package provides a managed [Hypertune](https://www.hypertune.com/) provider for the Flags SDK.

<LearnMore icon="arrow" href="/providers">
  Learn more about Adapters
</LearnMore>

## Getting started

Install the required dependencies:

```bash
pnpm i hypertune flags server-only @flags-sdk/hypertune @vercel/global-config
```

Set up Hypertune environment variables based on your framework and app structure:

```bash title=".env.local"
NEXT_PUBLIC_HYPERTUNE_TOKEN=token
HYPERTUNE_FRAMEWORK=nextApp
HYPERTUNE_OUTPUT_DIRECTORY_PATH=generated
```

Read more about type-safe client generation on [docs.hypertune.com](https://docs.hypertune.com/sdk-reference/type-safe-client-generation).

Run Hypertune's code generation with `npx hypertune`.

Use the generated code to declare your feature flags with `createHypertuneAdapter`:

```ts title="flags.ts"
import { createHypertuneAdapter } from '@flags-sdk/hypertune'
import { type Identify } from 'flags'
import { dedupe, flag } from 'flags/next'
/** Generated with `npx hypertune` */
import {
  createSource,
  flagFallbacks,
  vercelFlagDefinitions as flagDefinitions,
  type Context,
  type FlagValues,
} from './generated/hypertune'

const identify: Identify<Context> = dedupe(
  async ({ headers, cookies }) => {
    return {
      environment: process.env.NODE_ENV,
      user: {
        id: 'e23cc9a8-0287-40aa-8500-6802df91e56a',
        name: 'Example User',
        email: 'user@example.com',
      },
    }
  }
)

const hypertuneAdapter = createHypertuneAdapter<
  FlagValues,
  Context
>({ createSource, flagFallbacks, flagDefinitions, identify })

export const exampleFlag = flag(
  hypertuneAdapter.declarations.exampleFlag
)
```

Then use it in your framework:

```tsx title="app/page.tsx"
import { exampleFlag } from '@/flags'

export default async function Page() {
  const exampleFlagValue = await exampleFlag()

  return <div>Example Flag: {String(exampleFlagValue)}</div>
}
```

## Flags Explorer

You can inform the Flags Explorer about your Hypertune flags with a `.well-known/vercel/flags` route.

This lets you view and override your Hypertune flags using the [Flags Explorer](https://vercel.com/docs/flags/flags-explorer).

```ts title="app/.well-known/vercel/flags/route.ts#next"
import { createFlagsDiscoveryEndpoint } from 'flags/next'
/** Generated with `npx hypertune` */
import { vercelFlagDefinitions } from '../../../../generated/hypertune'

export const GET = createFlagsDiscoveryEndpoint(() => {
  return { definitions: vercelFlagDefinitions }
})
```

<LearnMore icon="arrow" href="https://vercel.com/docs/flags/flags-explorer" target="_blank">
  Learn more about the Flags Explorer
</LearnMore>

## How to configure Global Config

If Hypertune is syncing to Vercel Global Config, you can configure that through environment variables or through the adapter.

```bash title=".env.local"
EXPERIMENTATION_CONFIG="https://global-config.vercel.com/ecfg_xyz?token=abc"
EXPERIMENTATION_CONFIG_ITEM_KEY="hypertune_99999"
```

```ts title="flags.ts"
import { VercelEdgeConfigInitDataProvider as VercelGlobalConfigInitDataProvider } from 'hypertune'
import { createClient } from "@vercel/global-config"

const hypertuneAdapter = createHypertuneAdapter<
  FlagValues,
  Context
>({
  // ... previous initialization code
  createSourceOptions: {
    initDataProvider: new VercelGlobalConfigInitDataProvider({
      edgeConfigClient: createClient(
        'https://global-config.vercel.com/ecfg_xyz?token=abc'
      ),
      itemKey: 'hypertune_99999',
    }),
  },
})
```

## More resources

* [Hypertune Documentation](https://docs.hypertune.com/)
* [Vercel Global Config](https://vercel.com/docs/global-config)
* [Flags Explorer](https://vercel.com/docs/flags/flags-explorer)
* [Hypertune OpenFeature Provider](/providers/openfeature/hypertune)


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Providers
description: Combine your feature flag provider with the Flags SDK using an adapter.
---

# Providers



Integrate any feature flag provider with the Flags SDK
using an adapter. We publish adapters for the most common providers, but
it is also possible to write a [custom adapter](/providers/custom-adapters) in case we don't list
your provider or in case you have an in-house solution for feature flags.

## Featured providers

Featured providers offer fast setup using [Vercel Marketplace](https://vercel.com/marketplace/category/experimentation),
and low latency with Global Config. They also work well outside of Vercel in Next.js and SvelteKit projects.

<ProviderList featured />

## Additional providers

Additional providers are published under the `@flags-sdk` npm scope in the [Flags SDK repository](https://github.com/vercel/flags) and offer different levels of
integration with Next.js, SvelteKit, and Global Config.

<ProviderList featured={false} />

## Categories

* **Adapter** - Lets you evaluate feature flags and experiments using the Flags SDK.
* **Flags Explorer** - Displays flag metadata like descriptions in the [Flags Explorer](https://vercel.com/docs/flags/flags-explorer).
* **Marketplace** - Available in the Vercel Marketplace.
* **Global Config** - Lets you read feature flags from an Global Config.


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: LaunchDarkly
---

# LaunchDarkly



The [LaunchDarkly](https://launchdarkly.com/) provider contains support for LaunchDarkly's feature flags.

The `@flags-sdk/launchdarkly` package provides

* An [adapter](#provider-instance) for loading flags from LaunchDarkly.
* A `getProviderData` function for use with the Flags Explorer

<LearnMore icon="arrow" href="/providers">
  Learn more about Adapters
</LearnMore>

<LearnMore icon="arrow" href="https://vercel.com/docs/flags/flags-explorer" target="_blank">
  Learn more about the Flags Explorer
</LearnMore>

***

## Setup

The LaunchDarkly provider is available in the `@flags-sdk/launchdarkly` module. Install it with

```bash
npm install @flags-sdk/launchdarkly
```

***

## Provider instance

Import the default adapter instance `ldAdapter` from `@flags-sdk/launchdarkly`:

```ts
import { ldAdapter } from '@flags-sdk/launchdarkly';
```

If you need a customized setup, you can import `createLaunchDarklyAdapter` from `@flags-sdk/launchdarkly` and create an adapter instance with your settings:

```ts
import { createLaunchDarklyAdapter } from '@flags-sdk/launchdarkly';

const customLdAdapter = createLaunchDarklyAdapter({
  projectSlug: process.env.LAUNCHDARKLY_PROJECT_SLUG,
  clientSideId: process.env.LAUNCHDARKLY_CLIENT_SIDE_ID,
  // Legacy integrations that provide the connection string as `GLOBAL_CONFIG`
  // can pass it explicitly here.
  globalConfigConnectionString:
    process.env.EXPERIMENTATION_CONFIG ?? process.env.GLOBAL_CONFIG,
});
```

| Option key                     | Type     | Description                     |
| ------------------------------ | -------- | ------------------------------- |
| `projectSlug`                  | `string` | LaunchDarkly project slug       |
| `clientSideId`                 | `string` | LaunchDarkly client-side ID     |
| `globalConfigConnectionString` | `string` | Global Config connection string |

The default LaunchDarkly adapter configures itself based on the following environment variables:

* `LAUNCHDARKLY_CLIENT_SIDE_ID` *(required)* → `clientSideId`
* `LAUNCHDARKLY_PROJECT_SLUG` *(required)* → `projectSlug`
* `EXPERIMENTATION_CONFIG` *(required)* → `globalConfigConnectionString`

The native LaunchDarkly [Marketplace integration](https://vercel.com/marketplace/launchdarkly) exposes the Global Config connection string as `EXPERIMENTATION_CONFIG` when Global Config is enabled for the collection.

If you use the legacy LaunchDarkly Vercel integration, which provides the connection string as `GLOBAL_CONFIG`, set `EXPERIMENTATION_CONFIG` to the same value, or use `createLaunchDarklyAdapter` to pass `globalConfigConnectionString` explicitly.

***

## Identify users

LaunchDarkly relies on a [LaunchDarkly Evaluation Context](https://launchdarkly.com/docs/home/observability/contexts) object to evaluate the flags for a given request.

Use the `identify` function to determine a LaunchDarkly Evaluation Context.

```ts
import { dedupe, flag } from "flags/next";
import type { Identify } from "flags";
import { ldAdapter, type LDContext } from "@flags-sdk/launchdarkly";

const identify = dedupe((async ({ headers, cookies }) => {
  // Your own logic to identify the user
  // Identifying the user should rely on reading cookies and headers only, and
  // not make any network requests, as it's important to keep latency low here.
  const user = await getUser(headers, cookies);

  return {
    key: user.userID,
    // ... other properties
  };
}) satisfies Identify<LDContext>);

export const exampleFlag = flag<boolean, LDContext>({
  key: "example-flag",
  identify,
  adapter: ldAdapter.variation(),
});
```

<LearnMore icon="arrow" href="/frameworks/next/dedupe">
  Learn more about `dedupe`
</LearnMore>

<LearnMore icon="arrow" href="/principles/evaluation-context">
  Learn more about `identify`
</LearnMore>

***

## Methods

The LaunchDarkly adapter provides a method for evaluating flags.

### `variation`

```ts
export const exampleFlag = flag<boolean, LDContext>({
  key: 'example-flag',
  identify,
  adapter: ldAdapter.variation(),
});
```

The `identify` function must return the [Evaluation Context](https://launchdarkly.com/docs/home/observability/contexts).

### `ldClient`

The adapter exposes the LaunchDarkly client it uses internally through the `ldClient` property.

```ts
import { ldAdapter } from '@flags-sdk/launchdarkly';

ldAdapter.ldClient;
```

***

## Global Config

The LaunchDarkly adapter loads the configuration from [Global Config](https://vercel.com/storage/global-config).

Global Config is a global, ultra-low latency store which uses active replication and is specifically designed for serving feature flag configuration.

The default LaunchDarkly adapter, exported as `ldAdapter`, will automatically connect to Global Config when the required environment variables are set. It reads the connection string from `EXPERIMENTATION_CONFIG` (provided by the native Marketplace integration).

***

## Caveats

### Initializing

The Flags SDK automatically initializes the LaunchDarkly client when a flag is evaluated.

If you want to initialize the LaunchDarkly client before the first flag is used, you can call `ldAdapter.ldClient.waitForInitialization()` manually.

```ts
import { ldAdapter } from '@flags-sdk/launchdarkly';

// Somewhere in your server-side code
await ldAdapter.ldClient.waitForInitialization();
```

### LaunchDarkly Vercel SDK

The adapter uses the [LaunchDarkly Vercel SDK](https://launchdarkly.com/docs/sdk/edge/vercel) (`@launchdarkly/vercel-server-sdk`) internally, which is designed for usage with Global Config.

***

## Flags Explorer

View and override your LaunchDarkly flags using the [Flags Explorer](https://vercel.com/docs/flags/flags-explorer).

To make Flags Explorer aware of your LaunchDarkly flags, you need to provide a route which Flags Explorer will load your flags metadata from.

Use the `getProviderData` function  in your [Flags API endpoint](https://vercel.com/docs/workflow-collaboration/feature-flags/implement-flags-in-toolbar#creating-the-flags-api-endpoint) to load and emit your LaunchDarkly data. Accepts an `options` object with the following keys.

| Options key   | Type     | Description              |
| ------------- | -------- | ------------------------ |
| `apiKey`      | `string` | LaunchDarkly API Key     |
| `environment` | `string` | LaunchDarkly environment |
| `projectKey`  | `string` | LaunchDarkly project key |

```ts title="app/.well-known/vercel/flags/route.ts#next"
import { getProviderData, createFlagsDiscoveryEndpoint } from 'flags/next';
import { getProviderData as getLaunchDarklyProviderData } from '@flags-sdk/launchdarkly';
import { mergeProviderData } from 'flags';
import * as flags from '../../../../flags';

export const GET = createFlagsDiscoveryEndpoint(async (request) => {
  return mergeProviderData([
    getProviderData(flags),
    getLaunchDarklyProviderData({
      apiKey: process.env.LAUNCHDARKLY_API_KEY,
      projectKey: process.env.LAUNCHDARKLY_PROJECT_KEY,
      environment: process.env.LAUNCHDARKLY_ENVIRONMENT,
    }),
  ]);
});
```

## Read more

Read more about LaunchDarkly, Flags SDK, and the LaunchDarkly adapter.

* [Adapter Source Code](https://github.com/vercel/flags/tree/main/packages/adapter-launchdarkly)
* [Adapter Concept](/docs/adapters/supported-providers)
* [Precompute Concept](/principles/precompute)
* [LaunchDarkly Vercel SDK reference](https://launchdarkly.com/docs/sdk/edge/vercel)

<LearnMore icon="arrow" href="https://vercel.com/docs/flags/flags-explorer" target="_blank">
  Learn more about the Flags Explorer
</LearnMore>


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Optimizely
---

# Optimizely



The `@flags-sdk/optimizely` package provides

* An adapter for loading feature flags from this provider (coming soon)
* A `getProviderData` function for use with the Flags Explorer (available today)

<LearnMore icon="arrow" href="/providers">
  Learn more about Adapters
</LearnMore>

### getProviderData

A provider function to load flag metadata from Optimizely for use with the Flags Explorer. Accepts an `options` object with the following keys.

| Options key | Type     | Description           |
| ----------- | -------- | --------------------- |
| `apiKey`    | `string` | Optimizely API key    |
| `projectId` | `string` | Optimizely project ID |

```ts title="app/.well-known/vercel/flags/route.ts#next"
import { getProviderData, createFlagsDiscoveryEndpoint } from 'flags/next';
import { getProviderData as getOptimizelyProviderData } from '@flags-sdk/optimizely';
import { mergeProviderData } from 'flags';
import * as flags from '../../../../flags';

export const GET = createFlagsDiscoveryEndpoint(async (request) => {
  return mergeProviderData([
    getProviderData(flags),
    getOptimizelyProviderData({
      projectId: process.env.OPTIMIZELY_PROJECT_ID,
      apiKey: process.env.OPTIMIZELY_API_KEY,
    }),
  ]);
});
```

<LearnMore icon="arrow" href="https://vercel.com/docs/flags/flags-explorer" target="_blank">
  Learn more about the Flags Explorer
</LearnMore>


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: PostHog
---

# PostHog



The [PostHog](https://posthog.com/) package provides a managed PostHog adapter for the Flags SDK.

<LearnMore icon="arrow" href="/providers">
  Learn more about Adapters
</LearnMore>

<LearnMore icon="arrow" href="https://vercel.com/templates/edge-middleware/posthog-with-flags-sdk-and-next-js" target="_blank">
  Deploy the template
</LearnMore>

## Example usage

```tsx title="flags.ts"
import { flag } from "flags/next";
import { postHogAdapter } from '@flags-sdk/posthog'
import identify from "@/lib/identify";

// Reads the flag's evaluated value. Pass the adapter uninvoked
// (`postHogAdapter`) or invoked (`postHogAdapter()`) — both work.
export const myFlag = flag<boolean>({
  key: "posthog-flag",
  adapter: postHogAdapter,
  identify,
});

export const myFlagVariant = flag<string>({
  key: "posthog-multivariate-flag",
  adapter: postHogAdapter,
  identify,
});

// Reads the flag's attached payload with `.payload`.
export const myFlagPayload = flag({
  key: "posthog-flag-with-payload",
  adapter: postHogAdapter.payload,
  defaultValue: {},
  identify,
});
```

## Getting started

Install the required dependencies:

```bash
pnpm i @flags-sdk/posthog
```

### Environment variables

**Always required**, read by `postHogAdapter`:

```bash title=".env.local"
# The regional API host, which determines where your data lives
# Settings > Project > Project API Key
POSTHOG_HOST=https://us.i.posthog.com
# or https://eu.i.posthog.com

# Your project API key
# Settings > Project > Project API Key
POSTHOG_PROJECT_API_KEY=phc_...
```

**Optional**, opts `postHogAdapter` into [local evaluation](#local-evaluation), where
flag definitions are polled in the background instead of evaluating each flag remotely:

```bash title=".env.local"
# Settings > Project > Feature flags secret key
POSTHOG_SECRET_KEY=phs_...
```

**For the [Flags Explorer](#flags-explorer)**, read by `getProviderData` only:

```bash title=".env.local"
# Settings > User > Personal API keys
POSTHOG_PERSONAL_API_KEY=phx_...
# Settings > Project > Project ID
POSTHOG_PROJECT_ID=521742
```

Import the PostHog adapter for Flags SDK, which reads `POSTHOG_PROJECT_API_KEY`, `POSTHOG_HOST` and `POSTHOG_SECRET_KEY` when it is first used:

```ts title="flags.ts"
import { postHogAdapter } from '@flags-sdk/posthog'
```

If needed, you can instead initialize the adapter with your own options by importing `createPostHogAdapter`

```ts title="flags.ts"
import { createPostHogAdapter } from '@flags-sdk/posthog'

const postHogAdapter = createPostHogAdapter({
  postHogKey: process.env.POSTHOG_PROJECT_API_KEY!,
  postHogOptions: {
    host: process.env.POSTHOG_HOST,
    // ...
  },
})
```

The `postHogAdapter` is a single callable adapter. You can pass it directly, or
invoke it — both are equivalent:

* `postHogAdapter` (or `postHogAdapter()`): resolves the flag's evaluated value. For
  a boolean flag this is a boolean; for a multivariate flag it is the variant `string`.
  Type the flag (e.g. `flag<boolean>`) to get the value type you expect.
* `postHogAdapter.payload` (or `postHogAdapter.payload()`): resolves the flag's
  attached payload.

The flag's `key` is used as the PostHog feature flag key as-is. Every flag needs an
`identify` function returning the entities the adapter evaluates against:

```ts title="lib/identify.ts"
import type { Identify } from 'flags'
import type { PostHogEntities } from '@flags-sdk/posthog'

export const identify: Identify<PostHogEntities> = async () => {
  return { distinctId: 'user-123' }
}
```

The adapter throws if `entities` is missing, so a flag without `identify` will fail at
evaluation time.

```ts title="app/flags.ts"
import { flag } from 'flags/next'
import { postHogAdapter } from '@flags-sdk/posthog'
import { identify } from '@/lib/identify'

export const exampleFlag = flag<boolean>({
  key: 'example-flag',
  defaultValue: false,
  adapter: postHogAdapter,
  identify,
})
```

Flags backed by this adapter participate in [bulk evaluation](/frameworks/next/bulk-evaluation):
`evaluate()` resolves flags that share an `identify` source through a single PostHog
request.

Then use it in your framework:

```tsx title="app/page.tsx"
import { exampleFlag } from "@/flags";

export default async function Page() {
  const exampleValue = await exampleFlag();

  return <div>Example Flag: {String(exampleValue)}</div>;
}
```

## Evaluation modes

PostHog can evaluate flags remotely or locally.

### Remote evaluation (default)

With only `POSTHOG_PROJECT_API_KEY` and `POSTHOG_HOST` set, the adapter evaluates
remotely. PostHog makes a network request for every feature flag evaluation, and each request
is billed separately. Having to make a network request for every flag evaluation
also adds latency. If you provide user ids, PostHog will look up additional properties from its database.

### Local evaluation

Setting `POSTHOG_SECRET_KEY` (`phs_...`) is the only thing that switches evaluation
modes — the default adapter enables local evaluation exactly when that key is present.

In this mode, `posthog-node` periodically fetches your flag definitions in the
background (every 30s by default) and evaluates flags in-process against those cached
definitions, avoiding a network round trip on most checks after initialization. Because evaluation happens
locally, you're responsible for providing every property the flag's release conditions
depend on.

Each background poll is billed as 10 flag requests, independent of how many checks it
serves, so for a long-running process this is usually far cheaper than paying per
check. But the poller runs per compute instance, so PostHog recommends against local
evaluation in short-lived compute, where each invocation would otherwise
start its own poller and multiply cost rather than amortize it. Use remote evaluation
there instead. You can widen the polling interval to trade slower propagation of flag
changes for lower polling cost.

With [Fluid Compute](https://vercel.com/fluid) you may see latency and cost benefits from using local evaluation.
It depends on your traffic patterns and load.

You can also enable it explicitly with `createPostHogAdapter`:

```ts title="flags.ts"
import { createPostHogAdapter } from '@flags-sdk/posthog'

const postHogAdapter = createPostHogAdapter({
  postHogKey: process.env.POSTHOG_PROJECT_API_KEY!,
  postHogOptions: {
    host: process.env.POSTHOG_HOST,
    secretKey: process.env.POSTHOG_SECRET_KEY,
    enableLocalEvaluation: true,
  },
})
```

<Callout type="info">
  `POSTHOG_PERSONAL_API_KEY` is used only by the Flags Explorer (`getProviderData`,
  below) to discover flag definitions. It is never passed to the runtime client and
  does **not** enable local evaluation.
</Callout>

The default adapter also sets `disableGeoip: true`, since the server's IP is not a good
proxy for the user's location. Use `createPostHogAdapter` if you want the GeoIP-derived
person properties.

## Flags Explorer

### How to inform the Flags Explorer about flags

You can view and override these flags using the [Flags Explorer](https://vercel.com/docs/flags/flags-explorer)

For getProviderData, you will also need a personal API key and your project ID.

```bash title=".env.local"
# Settings > User > Personal API keys
POSTHOG_PERSONAL_API_KEY=phx_...
# Settings > Project > Project ID
POSTHOG_PROJECT_ID=521742
```

```ts title="app/.well-known/vercel/flags/route.ts#next"
import { createFlagsDiscoveryEndpoint } from 'flags/next'
import { getProviderData as getPostHogProviderData } from '@flags-sdk/posthog'

export const GET = createFlagsDiscoveryEndpoint(() => getPostHogProviderData({
  personalApiKey: process.env.POSTHOG_PERSONAL_API_KEY!,
  projectId: process.env.POSTHOG_PROJECT_ID!,
}))
```

`getProviderData` calls PostHog's app host, which it derives from `POSTHOG_HOST`
(`https://us.i.posthog.com` becomes `https://us.posthog.com`, and the EU host maps
accordingly). Pass `appHost` explicitly if you use a self-hosted or proxied instance.
Missing credentials or host are reported back as hints in the Flags Explorer rather
than throwing.

<LearnMore icon="arrow" href="https://vercel.com/docs/flags/flags-explorer" target="_blank">
  Learn more about the Flags Explorer
</LearnMore>

## Additional resources

* [Cutting Costs](https://posthog.com/docs/feature-flags/cutting-costs)
* [Local Evaluation](https://posthog.com/docs/feature-flags/local-evaluation)


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Reflag
---

# Reflag



The [Reflag](https://reflag.com/) provider contains support for Reflag's feature management tools.

Reflag is agent-ready feature flags for TypeScript.

The `@flags-sdk/reflag` provider package exports

* An [adapter](#provider-instance) for flags from Reflag.
* A [getProviderData](#flags-explorer) function for use with the Flags Explorer.

<LearnMore icon="arrow" href="/providers">
  Learn more about Adapters
</LearnMore>

<LearnMore icon="arrow" href="https://vercel.com/docs/flags/flags-explorer" target="_blank">
  Learn more about the Flags Explorer
</LearnMore>

<LearnMore icon="arrow" href="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fexamples%2Ftree%2Fmain%2Fflags-sdk/reflag&env=FLAGS_SECRET,REFLAG_SECRET_KEY&envDescription=The+FLAGS_SECRET+will+be+used+by+the+Flags+Explorer+to+securely+overwrite+feature+flags.+Must+be+32+random+bytes%2C+base64-encoded.+Use+the+generated+value+or+set+your+own.&envLink=https%3A%2F%2Fvercel.com%2Fdocs%2Fworkflow-collaboration%2Ffeature-flags%2Fsupporting-feature-flags%23flags_secret-environment-variable&project-name=reflag-flags-sdk-example&repository-name=reflag-flags-sdk-example" target="_blank">
  Deploy the Reflag template
</LearnMore>

***

## Setup

The Reflag provider is available in the `@flags-sdk/reflag` module. Install it with

```bash
npm install @flags-sdk/reflag
```

***

## Provider instance

Import the default adapter instance `reflagAdapter` from `@flags-sdk/reflag`:

```ts
import { reflagAdapter } from "@flags-sdk/reflag";
```

If you need a customized setup, you can import `createReflagAdapter` from `@flags-sdk/reflag` and create an adapter instance with your settings:

```ts
import { createReflagAdapter } from "@flags-sdk/reflag";

const reflagAdapter = createReflagAdapter({
  secretKey: process.env.REFLAG_SECRET_KEY,
});
```

See the [Reflag NodeSDK documentation](https://docs.reflag.com/supported-languages/node-sdk/globals#clientoptions) for the full list of options.

***

## Identify users and companies

The Reflag provider uses the `identify` property to identify users and companies. The `identify` function is called for every request to determine the user and company context.

Reflag relies on a setting a user/company to evaluate flags for a given request.

Set the `identify` property to a function which returns a Reflag `Context` containing user/company properties:

```ts
import { dedupe, flag } from "flags/next";
import type { Identify } from "flags";
import { reflagAdapter, type Context } from "@flags-sdk/reflag";

const identify = dedupe((async ({ headers, cookies }) => {
  // Your own logic to identify the user
  // Identifying the user should rely on reading cookies and headers only, and
  // not make any network requests, as it's important to keep latency low here.
  const user = await getUser(headers, cookies);

  return {
    user: {
      id: user.id,
      name: user.name,
      email: user.email,
    },
    company: {
      id: user.companyId,
    },
  } satisfies Context;
}) satisfies Identify<Context>);

export const myFeature = flag<boolean, Context>({
  key: "my_feature",
  identify,
  adapter: reflagAdapter.isEnabled(),
});
```

<LearnMore icon="arrow" href="/frameworks/next/dedupe">
  Learn more about `dedupe`
</LearnMore>

<LearnMore icon="arrow" href="/principles/evaluation-context">
  Learn more about `identify`
</LearnMore>

***

## Methods

### Feature toggling

Through the `featureIsEnabled` method, the Reflag provider supports determining if features are enabled/disabled.

```ts
export const myFeature = flag<boolean, Context>({
  key: "my_feature",
  adapter: reflagAdapter.isEnabled(),
  identify,
});
```

Remote Config, Adoption tracking and automatic feedback surveys are currently not supported in the Reflag Flags SDK provider.

***

### "Checks" events

Check events are used to log when a user is exposed to a feature.
Because middleware and server components are evaluated when routes are prefetched, check events are not supported in the Reflag Provider.

See the [Reflag React SDK](https://docs.reflag.com/supported-languages/next.js#client-side-rendering) documentation for more information on how to use check events in the client.

***

## Flags Explorer

View and override your Reflag feature toggles using the [Flags Explorer](https://vercel.com/docs/flags/flags-explorer).

To make Flags Explorer aware of your Reflag features, you need to provide a route which Flags Explorer will load your experiment metadata from.

Use the `getProviderData` function in your [Flags API endpoint](https://vercel.com/docs/workflow-collaboration/feature-flags/implement-flags-in-toolbar#creating-the-flags-api-endpoint) to load and emit your Reflag data.
`getProviderData` takes a `ReflagClient` in the `options` object:

```ts title="app/.well-known/vercel/flags/route.ts#next"
import { createFlagsDiscoveryEndpoint } from 'flags/next';
import { reflagAdapter, getProviderData } from "@flags-sdk/reflag";
import * as flags from '../../../../flags';

export const GET = createFlagsDiscoveryEndpoint(async () => {
  return getProviderData({
    reflagClient: await reflagAdapter.reflagClient(),
  });
});
```

## Read more

Read more about Reflag, Flags SDK, and the Reflag adapter.

* [Adapter Source Code](https://github.com/vercel/flags/tree/main/packages/adapter-reflag)
* [Adapter Concept](/docs/adapters/supported-providers)
* [Precompute Concept](/principles/precompute)
* [Reflag with Next.js](https://docs.reflag.com/supported-languages/next.js)
* [Reflag Node.js SDK on NPM](https://www.npmjs.com/package/@reflag/node-sdk)


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Split
---

# Split



The `@flags-sdk/split` package provides

* An adapter for loading feature flags from this provider (coming soon)
* A `getProviderData` function for use with the Flags Explorer (available today)

<LearnMore icon="arrow" href="/providers">
  Learn more about Adapters
</LearnMore>

### `getProviderData`

A provider function to load flag metadata from Statsig for use with the Flags Explorer. Accepts an `options` object with the following keys.

| Options key      | Type     | Description           |
| ---------------- | -------- | --------------------- |
| `adminApiKey`    | `string` | Split admin API key   |
| `workspaceId`    | `string` | Split workspace ID    |
| `organizationId` | `string` | Split organization ID |
| `environmentId`  | `string` | Split environment ID  |

```ts title="app/.well-known/vercel/flags/route.ts#next"
import { getProviderData, createFlagsDiscoveryEndpoint } from 'flags/next';
import { getProviderData as getSplitProviderData } from '@flags-sdk/split';
import { mergeProviderData } from 'flags';
import * as flags from '../../../../flags';

export const GET = createFlagsDiscoveryEndpoint(async (request) => {
  return mergeProviderData([
    getProviderData(flags),
    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,
    }),
  ]);
});
```

<LearnMore icon="arrow" href="https://vercel.com/docs/flags/flags-explorer" target="_blank">
  Learn more about the Flags Explorer
</LearnMore>


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Statsig
---

# Statsig



The [Statsig](https://statsig.com/) provider contains support for Statsig's feature management suite, including Feature Gates, Dynamic Config, Experiments, Autotune, and Layers.

Statsig helps you move faster with Feature Gates (Feature Flags) and Dynamic Configs. It also allows you to run A/B tests to validate your new features and understand their impact on your KPIs.

The `@flags-sdk/statsig` provider package exports

* An [adapter](#provider-instance) for loading experiments and flags from Statsig.
* A `getProviderData` function for use with the [Flags Explorer](#flags-explorer).

<LearnMore icon="arrow" href="/providers">
  Learn more about Adapters
</LearnMore>

<LearnMore icon="arrow" href="https://vercel.com/docs/flags/flags-explorer" target="_blank">
  Learn more about the Flags Explorer
</LearnMore>

<LearnMore icon="arrow" href="https://vercel.com/templates/next.js/statsig-experimentation-with-flags-sdk" target="_blank">
  Show the Statsig template
</LearnMore>

***

## Setup

The Statsig provider is available in the `@flags-sdk/statsig` module. Install it with

```bash
npm install @flags-sdk/statsig
```

***

## Provider instance

Import the default adapter instance `statsigAdapter` from `@flags-sdk/statsig`:

```ts
import { statsigAdapter } from '@flags-sdk/statsig';
```

If you need a customized setup, you can import `createStatsigAdapter` from `@flags-sdk/statsig` and create an adapter instance with your settings:

```ts
import { createStatsigAdapter } from '@flags-sdk/statsig';

const statsigAdapter = createStatsigAdapter({
  statsigServerApiKey: process.env.STATSIG_SERVER_API_KEY,
});
```

| Option key                      | Type             | Description                                                            |
| ------------------------------- | ---------------- | ---------------------------------------------------------------------- |
| `statsigServerApiKey`           | `string`         | Statsig server secret key                                              |
| `statsigOptions`                | `StatsigOptions` | Statsig initialization options                                         |
| `statsigProjectId`              | `string`         | Statsig project ID                                                     |
| `globalConfig`                  | `object`         | Global Config details for use with Statsig's Global Config integration |
| `globalConfig.connectionString` | `string`         | Global Config connection string                                        |
| `globalConfig.itemKey`          | `string`         | Key under which the Statsig configuration is stored in Global Config   |

The default statsig adapter configures itself based on the following environment variables:

* `STATSIG_SERVER_API_KEY` *(required)* → `statsigServerApiKey`
* `STATSIG_PROJECT_ID` *(optional)* → `statsigProjectId`
* `EXPERIMENTATION_CONFIG` *(optional)* → `globalConfig.connectionString`
* `EXPERIMENTATION_CONFIG_ITEM_KEY` *(optional)* → `globalConfig.itemKey`

***

## Identify users

Statsig relies on a [Statsig User](https://docs.statsig.com/concepts/user) object to evaluate the flags and experiments for a given request.

Use the `identify` function to determine a Statsig User.

```ts
import { dedupe, flag } from "flags/next";
import type { Identify } from "flags";
import { statsigAdapter, type StatsigUser } from "@flags-sdk/statsig";

const identify = dedupe((async ({ headers, cookies }) => {
  // Your own logic to identify the user
  // Identifying the user should rely on reading cookies and headers only, and
  // not make any network requests, as it's important to keep latency low here.
  const user = await getUser(headers, cookies);

  return {
    userID: user.userID,
    // ... other properties
  };
}) satisfies Identify<StatsigUser>);

export const myFeatureGate = flag<boolean, StatsigUser>({
  key: "my_feature_gate",
  identify,
  adapter: statsigAdapter.featureGate((gate) => gate.value),
});
```

<LearnMore icon="arrow" href="/frameworks/next/dedupe">
  Learn more about `dedupe`
</LearnMore>

<LearnMore icon="arrow" href="/principles/evaluation-context">
  Learn more about `identify`
</LearnMore>

***

## Methods

The Statsig adapter provides a method for each type of experiment or flag in Statsig.

### Feature Gates

| Parameter key | Type     | Description                                          |
| ------------- | -------- | ---------------------------------------------------- |
| `getter`      | function | Takes a Statsig `FeatureGate` and maps it to a value |

```ts
export const myFeatureGate = flag<boolean, StatsigUser>({
  key: 'my_feature_gate',
  adapter: statsigAdapter.featureGate((gate) => gate.value),
  identify,
});
```

The `key` is used to identify the Feature Gate in the Statsig console. Here it would resolve the `my_feature_gate` Feature Gate.

### Dynamic Configs

| Parameter key | Type     | Description                                            |
| ------------- | -------- | ------------------------------------------------------ |
| `getter`      | function | Takes a Statsig `DynamicConfig` and maps it to a value |

```ts
export const myDynamicConfig = flag<Record<string, unknown>, StatsigUser>({
  key: 'my_dynamic_config',
  adapter: statsigAdapter.dynamicConfig((config) => config.value),
  identify,
});
```

The `key` is used to identify the Dynamic Config in the Statsig console. Here it would resolve the `my_dynamic_config` Dynamic Config.

### Experiments

| Parameter key | Type     | Description                                            |
| ------------- | -------- | ------------------------------------------------------ |
| `getter`      | function | Takes a Statsig `DynamicConfig` and maps it to a value |

```ts
export const myExperiment = flag<Record<string, unknown>, StatsigUser>({
  key: 'my_experiment',
  adapter: statsigAdapter.experiment((config) => config.value),
  identify,
});
```

The `key` is used to identify the Experiment in the Statsig console. Here it would resolve the `my_experiment` Experiment. Statsig experiments return Dynamic Configs.

### Autotune

| Parameter key | Type     | Description                                            |
| ------------- | -------- | ------------------------------------------------------ |
| `getter`      | function | Takes a Statsig `DynamicConfig` and maps it to a value |

```ts
export const myAutotune = flag<Record<string, unknown>, StatsigUser>({
  key: 'my_autotune',
  adapter: statsigAdapter.autotune((config) => config.value),
  identify,
});
```

The `key` is used to identify the Autotune in the Statsig console. Here it would resolve the `my_autotune` Autotune. Statsig autotunes return Dynamic Configs.

### Layers

| Parameter key | Type     | Description                                    |
| ------------- | -------- | ---------------------------------------------- |
| `getter`      | function | Takes a Statsig `Layer` and maps it to a value |

```ts
export const myLayer = flag<Record<string, unknown>, StatsigUser>({
  key: 'my_layer',
  adapter: statsigAdapter.layer((layer) => layer.value),
  identify,
});
```

The `key` is used to identify the Autotune in the Statsig console. Here it would resolve the `my_layer` Layer.

***

## Bootstrapping

Bootstrapping refers to making the Statsig client used by the browser aware of the configuration and user the server used when evaluating feature flags and experiments.

Using experiments and flags server side allows the initial page render to respect the feature flags and experiments, which avoids layout shift. When feature flags and experiments are used server-side the client must still be made aware of them to log exposures and track events.

### Dynamic pages

When using server-side rendering the server can inline the information needed to bootstrap the client. This allows the client to log exposures and track events without having to make a network request to initialize itself.

Your application roughly needs to follow these steps:

1. Call the same `identify` function your feature flags use to get the Statsig user.
2. Call `statsigAdapter.initialize()` to initialize the `statsig-node-lite` SDK.
3. Prepare the bootstrap data on the server, and pass it to the client.
4. Use the bootstrap data on the browser to initialize a client and set up the Statsig React provider.

Below are the most critical pieces you need to implement this, which is meant as a starting point.

**Inlining the bootstrap data on the server**

```tsx title="app/(example)/layout.tsx#next"
import { cookies, headers } from "next/headers";
import { statsigAdapter } from "@flags-sdk/statsig";
import { DynamicStatsigProvider } from "./dynamic-statsig-provider";
// The same identify function you use when declaring flags
// See https://flags-sdk.dev/docs/api-reference/adapters/statsig#identify-users
import { identify } from "../../identify";

export default async function Layout({
  children,
}: {
  children: React.ReactNode;
}) {
  const [headersStore, cookieStore] = await Promise.all([headers(), cookies()]);
  const user = await identify({ headers: headersStore, cookies: cookieStore });

  // Get a reference to the Statsig SDK instance configured by the adapter
  const Statsig = await statsigAdapter.initialize();

  // Prepare the bootstrap data on the server, and pass it to the client
  const datafile = await Statsig.getClientInitializeResponse(user, {
    hash: "djb2", // must use this hash function for compatibility with the client
  });

  return (
    <DynamicStatsigProvider datafile={datafile}>
      {children}
    </DynamicStatsigProvider>
  );
}
```

**Reading the bootstrap data on the client**

```tsx title="app/(example)/dynamic-statsig-provider.tsx#next"
"use client";

import { useMemo } from "react";
import type { Statsig } from "@flags-sdk/statsig";
import {
  StatsigProvider,
  useClientBootstrapInit,
} from "@statsig/react-bindings";

export function DynamicStatsigProvider({
  children,
  datafile,
}: {
  children: React.ReactNode;
  datafile: Awaited<ReturnType<typeof Statsig.getClientInitializeResponse>>;
}) {
  if (!datafile) throw new Error("Missing datafile");

  // Statsig expects a stringified datafile, but ideally the Statsig SDK
  // would accept a JSON object so we could avoid this stringification.
  const datafileString = useMemo(() => JSON.stringify(datafile), [datafile]);

  const client = useClientBootstrapInit(
    process.env.NEXT_PUBLIC_STATSIG_CLIENT_KEY as string,
    datafile.user,
    datafileString
    // NOTE you could provide the Autocapture plugin here
  );

  return (
    <StatsigProvider user={datafile.user} client={client}>
      {children}
    </StatsigProvider>
  );
}
```

**Use the client to log exposures and events**

```tsx title="app/(example)/page.tsx#next"
"use client";

import { useEffect } from "react";
import { useStatsigClient } from "@statsig/react-bindings";

export default function Page() {
  const statsigClient = useStatsigClient();

    // Manually log the exposure on mount
  useEffect(() => {
    statsigClient.getDynamicConfig("my_dynamic_config");
  }, [statsigClient]);

  return (
    <button
      className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
      type="button"
      onClick={() => {
        // Manually log an event
        statsigClient.logEvent("click_button");
      }}
    >
      Click me
    </button>
  );
}
```

### Static pages

When using the [precompute](/principles/precompute) pattern a static prerender will be created for each variant of the page. For example, a page using two boolean feature flags will exist in four variants.

Each variant may only contain the state of feature flags, but no user specific information. Otherwise the prerender could not be shared between multiple users. This forces you to bootstrap the Statsig client differently than when using [dynamic pages](#dynamic-pages).

The initial static page must depend on feature flag and experiment configuration values only, but may not contain any user specific information. This allows the page to be served statically from the CDN and allows the initial render to be correct.

The client running in the browser must bootstrap the Statsig client over the network because of this.

Steps

1. Create an API route which returns the bootstrap data und user information needed by the Statsig client
2. Bootstrap the Statsig client in the browser using the data returned from the API route.

See [this example](https://github.com/vercel/examples/tree/main/flags-sdk/experimentation-statsig) for a reference implementation.

Read more about initialization strategies in the [Statsig Docs](https://docs.statsig.com/client/javascript-sdk/init-strategies)

***

## Global Config

The Statsig adapter can either load the experiment configuration over the network or bootstrap from [Global Config](https://vercel.com/storage/global-config).

Using [Global Config](https://vercel.com/docs/storage/global-config) is optional but recommended for the best latency. Global Config is a global, ultra-low latency store which uses active replication and is specifically designed for serving feature flag configuration.

The default Statsig adapter, exported as `statsigAdapter`, will automatically connect to Global Config if the `EXPERIMENTATION_CONFIG` and `EXPERIMENTATION_CONFIG_ITEM_KEY` environment variables are set. If you are using the [Statsig integration on Vercel marketplace](https://vercel.com/marketplace/statsig) these environment variables will be provided automatically, and the default Statsig adapter will read from Global Config automatically.

***

## Caveats

### Initializing

The Flags SDK automatically initializes the Statsig client when a flag is evaluated.

If you want to initialize the Statsig client before the first flag is used, you can call `statsigAdapter.initialize` manually. Further,
use the manual call to initialize `statsig-node-lite` for usage with other server-side code.

```ts
import { statsigAdapter, Statsig } from '@flags-sdk/statsig';

const statsigInitializationPromise = statsigAdapter.initialize();

export async function getStatsigExperiment(key: string) {
  await statsigInitializationPromise;
  return Statsig.getExperimentSync(key);
}
```

Use `statsigAdapter.initialize` instead of `Statsig.initialize` as it configures the Statsig client specifically for Flags SDK compatibility.

### Same key with different mapping functions

A Dynamic Config in Statsig can store arbitrary JSON objects. To create multiple flags that access different parts of the same config, use a shared key prefix followed by a unique name.

In the example below, both flags reference `my_config`, each with a distinct key and mapping function.

The `.` character is used to differentiate flags. The part before the dot identifies the dynamic config, while the second part distinguishes the flags.

```ts
export const myDynamicText = flag<string, StatsigUser>({
  // Will retrieve `my_config` from Statsig
  key: 'my_config.text',
  adapter: statsigAdapter.dynamicConfig(
    (config) => config.value.text as string,
  ),
  identify,
});

export const myDynamicPrice = flag<number, StatsigUser>({
  // Will retrieve `my_config` from Statsig
  key: 'my_config.price',
  adapter: statsigAdapter.dynamicConfig(
    (config) => config.value.price as number,
  ),
  identify,
});
```

### Statsig Node Lite

The adapter uses `statsig-node-lite`, which is a slimmed version of the Statsig Node.js SDK optimized for server side and Routing Middleware usage.

### Exposure logging

Because middleware and server components are evaluated when routes are prefetched, exposures are not logged by default. You can enable exposure logging by providing the `exposureLogging` option to the adapter functions.

```ts
export const exampleFlag = flag<boolean, StatsigUser>({
  key: "new_feature_gate",
  ...
  adapter: statsigAdapter.featureGate((gate) => gate.value, {
    exposureLogging: true,
  })
});
```

When logging is on, your application should also call `Statsig.flush` appropriately to ensure exposures are recorded.

The recommended approach for experimentation is to log exposures from the client when
the user is indeed exposed to an experiment, either when seen or interacted with.

[Read about Statsig's React Bindings](https://docs.statsig.com/client/javascript-sdk/react#basics-get-experiment)

***

## Flags Explorer

View and override your Statsig experiments using [Flags Explorer](https://vercel.com/docs/flags/flags-explorer).

To make Flags Explorer aware of your Statsig experiments, you need to provide a route which Flags Explorer will load your experiment metadata from.

Use the `getProviderData` function  in your [Flags API endpoint](https://vercel.com/docs/workflow-collaboration/feature-flags/implement-flags-in-toolbar#creating-the-flags-api-endpoint) to load and emit your Statsig data. Accepts an `options` object with the following keys.

| Options key     | Type     | Description             |
| --------------- | -------- | ----------------------- |
| `consoleApiKey` | `string` | Statsig console API key |
| `projectId`     | `string` | StatSig project ID      |

```ts title="app/.well-known/vercel/flags/route.ts#next"
import { getProviderData, createFlagsDiscoveryEndpoint } from 'flags/next';
import { getProviderData as getStatsigProviderData } from '@flags-sdk/statsig';
import { mergeProviderData } from 'flags';
import * as flags from '../../../../flags';

export const GET = createFlagsDiscoveryEndpoint(async (request) => {
  return mergeProviderData([
    getProviderData(flags),
    getStatsigProviderData({
      consoleApiKey: process.env.STATSIG_CONSOLE_API_KEY,
      projectId: process.env.STATSIG_PROJECT_ID,
    }),
  ]);
});
```

## Read more

Read more about Statsig, Flags SDK, and the Statsig adapter.

* [Adapter Source Code](https://github.com/vercel/flags/tree/main/packages/adapter-statsig)
* [Adapter Concept](/docs/adapters/supported-providers)
* [Precompute Concept](/principles/precompute)
* [Statsig with Next.js](https://docs.statsig.com/client/javascript-sdk/next-js/)
* [Statsig Node Lite on NPM](https://www.npmjs.com/package/statsig-node-lite)


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Vercel Flags
---

# Vercel Flags



Vercel Flags is a feature flag provider built into Vercel's platform, allowing you to manage flags directly in your Vercel projects. The `@flags-sdk/vercel` package connects the Flags SDK to Vercel Flags.

<CopyPrompt text="Connect my existing Flags SDK setup to Vercel Flags. Install `@flags-sdk/vercel`, pull the `FLAGS` and `FLAGS_SECRET` environment variables, update my flag declarations to use `vercelAdapter`, add or reuse an `identify` function for targeting entities, create the Flags Explorer discovery endpoint, and run the relevant type checks or build when finished.">
  Connect my existing Flags SDK setup to Vercel Flags. Install `@flags-sdk/vercel`, pull the `FLAGS` and `FLAGS_SECRET` environment variables, update my flag declarations to use `vercelAdapter`, add or reuse an `identify` function for targeting entities, create the Flags Explorer discovery endpoint, and run the relevant type checks or build when finished.
</CopyPrompt>

<LearnMore icon="arrow" href="https://vercel.com/blog/vercel-flags-platform-native-feature-flags" target="_blank">
  Vercel Flags is now available — read the announcement
</LearnMore>

<LearnMore icon="arrow" href="https://vercel.com/docs/flags/vercel-flags" target="_blank">
  Learn more about Vercel Flags
</LearnMore>

## Installation

Install the Vercel adapter package:

```bash
pnpm i flags @flags-sdk/vercel
```

## Quickstart

### 1. Create a feature flag in Vercel

Create your first feature flag in the Vercel dashboard:

<LearnMore icon="arrow" target="_blank" href="https://vercel.com/d?to=%2F%5Bteam%5D%2F%5Bproject%5D%2Fflags%3Fcreate%3D1&title=Go+to+Vercel+Flags">
  Create Flag
</LearnMore>

You can create a boolean feature flag called `example-flag` for this quickstart.

When you create a flag, Vercel automatically configures these environment variables:

* `FLAGS` - SDK Key for your Vercel Flags project
* `FLAGS_SECRET` - Secret key used by Flags Explorer for flag overrides

### 2. Sync environment variables

Pull the environment variables to your local project using [Vercel CLI](https://vercel.com/docs/cli):

```bash
vercel env pull
```

You might need to run `vercel link` first in case you did not link your project to Vercel yet.

### 3. Declare the flag in your code

Use the `vercelAdapter` to connect your flag declaration to Vercel Flags:

```ts title="flags.ts"
import { flag } from 'flags/next';
import { vercelAdapter } from '@flags-sdk/vercel';

export const exampleFlag = flag({
  key: "example-flag",
  adapter: vercelAdapter
});
```

The `vercelAdapter` automatically uses the `FLAGS` environment variable to connect to your Vercel Flags project.

### 4. Use the flag

Call the flag as a function to resolve its value:

```tsx title="app/hello-world/page.tsx#next"
import { exampleFlag } from '../../flags';

export default async function Page() {
  const showExample = await exampleFlag();

  return <div>{showExample ? 'Hello world' : 'Not showing'}</div>
}
```

You can now toggle the feature flag for the development environment on the Vercel dashboard and you should see the updated flag value locally after reloading the page.

## User targeting

Target specific users or groups by providing an `identify` function that returns context about the current user, team, or other entities.

```ts title="flags.ts"
import { dedupe, flag } from 'flags/next';
import { vercelAdapter } from '@flags-sdk/vercel';

type Entities = {
  team?: { id: string };
  user?: { id: string };
};

// Use dedupe to prevent redundant evaluation context lookups
// https://flags-sdk.dev/frameworks/next/evaluation-context#deduplication
const identify = dedupe(async (): Promise<Entities> => {
  return {
    team: { id: 'team-123' },
    user: { id: 'user-456' },
  };
});

export const exampleFlag = flag<boolean, Entities>({
  key: "example-flag",
  identify,
  adapter: vercelAdapter
});
```

Configure which entities are available for targeting in your Vercel Flags project:

<LearnMore icon="arrow" target="_blank" href="https://vercel.com/d?to=%2F%5Bteam%5D%2F%5Bproject%5D%2Fflags%2F%5Bentities&title=Go+to+Vercel+Flags">
  Configure Entities
</LearnMore>

This allows you to create targeting rules in Vercel Flags based on your specific entity types (users, teams, organizations, etc.).

## Flags Explorer integration

[Flags Explorer](https://vercel.com/docs/flags/flags-explorer) provides a UI for viewing and overriding flags during development and testing.

To enable full integration, create a flags discovery endpoint:

```ts title="app/.well-known/vercel/flags/route.ts#next"
import { createFlagsDiscoveryEndpoint } from 'flags/next';
import { getProviderData } from "@flags-sdk/vercel";
import * as flags from '../../../../flags';

export const GET = createFlagsDiscoveryEndpoint(async (request) => {
  return await getProviderData(flags);
});
```

This endpoint allows Flags Explorer to:

* Discover all flags defined in your codebase
* Show flag metadata and default values
* Enable local overrides during development

## Advanced configuration

### Custom adapter configuration

To connect to a different Vercel Flags project or use a custom SDK Key:

```ts title="flags.ts"
import { flag } from 'flags/next';
import { createVercelAdapter } from '@flags-sdk/vercel';

const customAdapter = createVercelAdapter(
  process.env.CUSTOM_FLAGS_KEY!
);

export const exampleFlag = flag({
  key: "example-flag",
  adapter: customAdapter
});
```

## Additional resources

* [Vercel Flags](https://vercel.com/docs/flags/vercel-flags)
* [@flags-sdk/vercel](https://github.com/vercel/flags/tree/main/packages/adapter-vercel)


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Data Locality
---

# Data Locality



Where feature flags are evaluated, and what data they need, determines
the latency you pay to evaluate them.

To evaluate feature flags, two types of data are needed:

* The **definition** contains the rules for evaluating the feature,
  such as who the feature flag should be enabled for. This is typically
  loaded from a feature flag provider.
* The **evaluation context** is data about the user or entity the feature flags are evaluated for.

A feature flag evaluation can be thought of like this:

```
evaluate(definition, evaluation context) = value
```

Evaluating a feature flag requires the definition and the evaluation
context.

A feature flag that is on or off for all users does not need an
evaluation context. A feature flag that is only on for some users needs
an evaluation context.

## Where feature flags can be evaluated

* **Server-side** runs in the [Serverless Function Region](https://vercel.com/docs/functions/configuring-functions/region) configured for the project and is used for React Server Components, API Routes, and Server Actions.
* **Routing Middleware** runs globally, in a CDN.
* **Client-side** runs in your browser.

There are different considerations for how the definitions and the
evaluation context are loaded or established. For example, it has
disastrous consequences if an application needs to make a network
request from Routing Middleware in order to establish the current user for
the evaluation context.

## How feature flag definitions are loaded

Feature flag SDKs initially need to bootstrap the feature flag
definitions. Typically this happens using a network request. They then
typically establish a websocket connection to get notified about any
changes to the feature flag configuration in the flag provider.

This model works well with long-running servers, but is not a great fit
for the serverless world. Serverless functions have a much shorter
lifetime than long-running servers, especially at the edge. This means
applications need to pay the latency cost of bootstrapping feature flags
more frequently. Having multiple websocket connections to the same flag
provider also increases load on the provider.

## Vercel Global Config

Vercel offers a solution called Global Config to this problem. It is
specifically designed for storing feature flag definitions. Global Config
can be read in under 1ms at p90 and under 15ms at p95, including the
network latency from your Serverless Function or Routing Middleware.

To put this into perspective, [according to this benchmark](https://github.com/dvassallo/s3-benchmark), an AWS S3 bucket does not even send the first byte by the time an Edge
Config is fully read.

Using Global Config is optional, but highly recommended, when using the
Flags SDK.

## Evaluating on the server

Feature flags can be evaluated on the server using the Flags SDK. This
is the most common way to evaluate feature flags, and the most
straightforward to implement.

The serverless function region is typically close to your
application's database, so it is somewhat okay to make a network
request to establish the evaluation context.

## Evaluating at the edge

To evaluate feature flags at the edge, you need the definition and the
evaluation context available at the edge.

Using Global Config allows storing definitions at the Edge as shown in the
previous section. This means you can use feature flags in Routing
Functions at ultra low latency.

However, some feature flags might need an evaluation context in order to
evaluate. Since the evaluation context depends on the application it is
up to the application to provide it at low latency.

Making a network request or reading a database to get the evaluation
context inside of Routing Middleware should be avoided at all costs.

Instead, it is wise to store the information necessary to evaluate
feature flags in a cookie when users sign into an application. The
browser will then forward the necessary information when making
requests, such that Routing Middleware can establish the evaluation context
based on the provided cookie. Where necessary, the cookie stored on the
client can be signed or even encrypted to avoid manipulation or leaking
information.

## Deduplicating effort

No matter whether feature flags are evaluated in Serverless Functions or
in Routing Middleware it is wise to deduplicate the effort of establishing
the evaluation context.

<LearnMore href="/frameworks/next/dedupe" icon="arrow">
  Learn more about `dedupe`
</LearnMore>

## Evaluating on the client

Feature flags can also be evaluated on the client. The Flags SDK does
not have a built-in pattern for doing so currently.

It is however possible to evaluate feature flags on the server and pass
the evaluated value down to the client.

There is also a pattern, independent of the Flags SDK, which is
recommended in case you must absolutely use client-side feature flags.


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Evaluation Context
description: Segment by any criteria, using an evaluation context.
---

# Evaluation Context



import Link from 'next/link';

It is common for features to be on for some users, but off for others.
For example team members working on a new setting might need to see and
use the setting, while the rest of the team need the setting to be
hidden.

The `flag` declaration accepts an `identify`
function. The entities returned from the `identify` function
are passed as an argument to the `decide` function.

## Example

A trivial case to illustrate the concept:

```tsx title="flags.tsx#next"
import { flag } from 'flags/next';

export const exampleFlag = flag<boolean>({
  key: 'identify-example-flag',
  identify() {
    return { user: { id: 'user1' } };
  },
  decide({ entities }) {
    return entities?.user?.id === 'user1';
  },
});
```

<IframeBrowser src="snippets:/concepts/identify/basic" codeSrc="https://github.com/vercel/flags/tree/main/examples/snippets/app/concepts/identify/basic" />

Having first-class support for an evaluation context allows decoupling
the identifying step from the decision making step.

## Type safety

The entities can be typed using the `flag` function.

```tsx title="flags.tsx#next"
import { flag } from 'flags/next';

interface Entities {
  user?: { id: string };
}

export const exampleFlag = flag<boolean, Entities>({
  key: 'identify-example-flag',
  identify() {
    return { user: { id: 'user1' } };
  },
  decide({ entities }) {
    return entities?.user?.id === 'user1';
  },
});
```

## Headers and cookies

The `identify` function is called with `headers`{" "}
and `cookies` arguments, which is useful when dealing with
anonymous or authenticated users.

The arguments are normalized to a common format so the same flag can be
used in Routing Middleware, App Router, and Pages Router without having to
worry about the differences in how `headers` and{" "}
`cookies` are represented there.

```tsx title="flags.tsx#next"
import { flag } from 'flags/next';

export const exampleFlag = flag<boolean, Entities>({
  // ...
  identify({ headers, cookies }) {
    // access to normalized headers and cookies here
    headers.get('auth');
    cookies.get('auth')?.value;
    // ...
  },
  // ...
});
```

## Deduplication

The `dedupe` function is a helper to prevent duplicate work.

Any function wrapped in `dedupe` will only ever run once for
the same request within the same runtime and given the same arguments.

This helper is useful in combination with the{" "}
`identify` function, as it allows the identification to only
happen once per request. This is useful in preventing overhead when
passing the same `identify` function to multiple feature
flags.

<LearnMore href="/frameworks/next/dedupe" icon="arrow">
  Learn more about `dedupe`
</LearnMore>

## Precomputing and targeting

The [Marketing Pages](/docs/guides/marketing-pages) example which shows how to identify and target users using cookies when
precomputing pages.

## Custom evaluation context

While it is best practice to let the `identify` function
determine the evaluation context, it is possible to provide a custom
evaluation context.

```tsx
// pass a custom evaluation context from the call site
await exampleFlag.run({ identify: { user: { id: 'user1' } } });

// pass a custom evaluation context function from the call site
await exampleFlag.run({ identify: () => ({ user: { id: 'user1' } }) });
```

This should be used sparsely, as custom evaluation context can make
feature flags less predictable across your code base.

### Full example

The example below shows how to use the `identify` function to
display different content to different users.

<IframeBrowser src="snippets:/concepts/identify/full" codeSrc="https://github.com/vercel/flags/tree/main/examples/snippets/app/concepts/identify/full" />

The above example is implemented using this feature flag:

```tsx title="flags.tsx#next"
import type { ReadonlyRequestCookies } from 'flags';
import { dedupe, flag } from 'flags/next';

interface Entities {
  user?: { id: string };
}

const identify = dedupe(
  ({ cookies }: { cookies: ReadonlyRequestCookies }): Entities => {
    // This could read a JWT instead
    const userId = cookies.get('identify-example-user-id')?.value;
    return { user: userId ? { id: userId } : undefined };
  },
);

export const identifyExampleFlag = flag<boolean, Entities>({
  key: 'identify-example-flag',
  identify,
  decide({ entities }) {
    if (!entities?.user) return false;
    return entities.user.id === 'user1';
  },
});
```

<LearnMore href="/frameworks/next/guides/marketing-pages" icon="arrow">
  See the Marketing Pages example
</LearnMore>


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Flags as Code
---

# Flags as Code



The Flags SDK is conceptually different from the SDKs of most feature
flag providers. It puts a few constraints in place that lead to a better
experience when using flags.

## Consistent, predictable call sites

Using the SDK of a typical feature flag provider looks similar to the
code example below, where the SDK is called with the name of the feature
flag as a string:

```tsx
// a typical feature flag SDK, such as OpenFeature in this example
const exampleValue = await client.getBooleanValue('exampleFlag', false);
```

Compare this to what it looks like when using a feature flag in the
Flags SDK:

```tsx
// the Flags SDK
const exampleValue = await exampleFlag();
```

Using a feature flag first requires declaring it like so:

```tsx
import { flag } from 'flags/next';

export const exampleFlag = flag({
  key: 'example-flag',
  defaultValue: false,
  decide() {
    return false;
  },
});
```

## Feature flags are functions

Turning each feature flag into its own function means the implementation
can change without having to touch the call site. It also lets you
use your well-known editor shortcuts like "Find All References" to see
if a flag is still in use.

## Feature flags declare their default value

Each feature flag's declaration can contain the default value. This
value is used in case the feature flag can not be evaluated. Containing
the default value on the declaration means it will be consistent across
all evaluations.

## Feature flags declare how their context is established

Traditional feature flag SDKs require passing in the context on the call
site, as shown below:

```tsx
// a typical feature flag SDK, such as OpenFeature in this example

// add a value to the invocation context
const context = {
  user: { id: '123' },
};

const boolValue = await client.getBooleanValue('boolFlag', false, context);
```

The downside of this approach is that every call site needs to recreate
the evaluation context. If the evaluation context is created differently
or not provided, the feature flag may evaluate differently across the
codebase.

The Flags SDK does not require the context to be passed in on each
invocation. Instead, the context is established when you declare the
feature flag.

```tsx title="flags.ts#next"
import { flag } from 'flags/next';

export const exampleFlag = flag({
  key: 'example-flag',
  identify() {
    return { user: { id: '123' } };
  },
  decide({ entities }) {
    return entities.user.id === '123';
  },
});
```

<LearnMore href="/principles/evaluation-context" icon="arrow">
  Learn more about `identify`
</LearnMore>

## Avoid vendor lock-in

A downside of using the SDK of a specific provider is that it makes it
hard to switch to a different feature flag provider at a later point.
Often, the provider's SDK becomes deeply integrated into the
codebase over time.

The Flags SDK does not lock you into a specific provider. You can switch
to a different provider by changing the definition of the feature flag.
Switching providers is possible without changing where your feature flag
is used.

The Flags SDK further contains an adapter pattern for this, which
streamlines swapping providers.

```tsx title="flags.ts#next"
import { flag } from 'flags/next';
import { statsigAdapter } from '@flags-sdk/statsig';

export const exampleFlag = flag({
  key: 'example-flag',
  // You can replace the adapter with a different one
  // This example loads a feature gate from Statsig
  adapter: statsigAdapter.featureGate((config) => config.value),
});
```

<LearnMore href="/providers" icon="arrow">
  Learn more about `adapters`
</LearnMore>


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Precompute
description: Precomputing describes a pattern where Routing Middleware uses feature flags to decide which variant of a page to show. This allows you to keep the page itself static, which leads to low latency globally as the page can be served from the CDN.
---

# Precompute





<img alt="Precompute manual" src={__img0} placeholder="blur" />

With precompute, you can:

* Combine multiple feature flags on a single, static page.
* Use middleware to make routing decisions.
* Generate pages for each flag combination at build time or lazily, the first time it's accessed.
* Cache pages with Incremental Static Regeneration (ISR).

Precompute works by using dynamic route segments to transport an encoded version of the feature flags computed within Routing Middleware. Encoding the values within the URL allows the page itself to access the precomputed values, and also ensures there is a unique URL for each combination of feature flags on a page. Because the system works using rewrites, the visitor will never see the URL containing the flags. They will only see the clean, original URL.

Rewriting to static variants of a page can be done manually, but this quickly becomes cumbersome as the number of feature flags and pages increase. To address this, the Flags SDK offers helper functions which allow precomputing multiple flags at once, and then accessing the precomputed values from a page.

<LearnMore href="/frameworks/next/precompute" icon="arrow">
  <div>
    Learn how to implement the precompute pattern in Next.js
  </div>
</LearnMore>

<LearnMore href="/frameworks/sveltekit/precompute" icon="arrow">
  <div>
    Learn how to implement the precompute pattern in SvelteKit
  </div>
</LearnMore>


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Server-side vs Client-side
---

# Server-side vs Client-side



We strongly believe feature flags should be used server-side.

## Avoid layout shift and jank

When a feature flag is used on the client side, it can cause layout
shifts and jank. The application needs to wait until the feature flags
are bootstrapped over the network. In the meantime it has to take one of
two bad choices:

* Show a loading spinner
* Speculatively show one version of the page

But if the feature flag turns out to have a different value, the page
needs to be swapped out leading to jank.

When you use feature flags server-side, this problem is avoided. The
server will only send the version of the page that matches the feature
flag. No layout shift or jank.

## Keeping pages static

A big benefit using client-side feature flags is that the page itself
can stay fully static. Having static pages is great, as they can be
served from the CDN around the world at low latency.

A common misconception is that server-side usage of feature flags means
that the page can no longer be static. This is not the case. The Flags
SDK comes with multiple patterns which allow keeping the page static
without falling back to client-side usage.

These patterns are made possible by using Routing Middleware. One or
multiple feature flags can be evaluated in Routing Middleware, and the
request can then be rewritten to serve a statically generated version of
the page. This combines extremely well with Incremental Static
Regeneration (ISR).

<LearnMore href="/frameworks/next/guides/marketing-pages" icon="arrow">
  See the Marketing Pages example
</LearnMore>

## Confidentiality

Using feature flags on the client typically means the name of the
feature flag is sent to the client. Often times teams then fall back to
using cryptic alias for their feature flags in order to avoid leaking
features.

## Code size

When feature flags are used server-side, only the necessary code is sent
to the client. In contrast, when using feature flags client-side it is
common that both versions of the page are sent to the client, which
leads to an increased bundle size.


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: flags
description: The `flags` package provides the core functionality for integrating with Vercel.
---

# flags



### `verifyAccess`

A method for verifying access to your flags endpoint based on encrypted tokens (JWEs) generated by the SDK functions documented below.
You can use `verifyAccess` to keep your endpoint private, but allow the Vercel Toolbar to access it. Returns a `Promise` with a `true` or `false` value.

If you are using Next.js App Router you should use [`createFlagsDiscoveryEndpoint`](/api-reference/frameworks/next#createflagsdiscoveryendpoint) instead, which will create the full route handler.

| Parameter       | Type     | Description                   |
| --------------- | -------- | ----------------------------- |
| `authorization` | `string` | Authorization token to verify |

```tsx title="app/.well-known/vercel/flags/route.ts#next"
import { NextResponse, type NextRequest } from "next/server";
import { verifyAccess, version, type ProviderData, type ApiData } from "flags";

export async function GET(request: NextRequest) {
  const access = await verifyAccess(request.headers.get('Authorization'));
  if (!access) return NextResponse.json(null, { status: 401 });

  const providerData: ProviderData = /* ... */

  return NextResponse.json<ApiData>(providerData, {
    headers: {
      'x-flags-sdk-version': version,
      'cache-control': 'no-store',
    },
  });
}
```

If you are using the `Pages` router, you will need to add the following to your `next.config.js`. This is because the `Pages` router can't specify API routes outside of the `api` folder. This means you need a [rewrite](https://nextjs.org/docs/pages/api-reference/next-config-js/rewrites).

```tsx title="pages/api/vercel/flags.ts#next"
import type { NextApiRequest, NextApiResponse } from "next";
import { type ProviderData, verifyAccess } from "flags";

export async function handler(request: NextApiRequest, response: NextApiResponse) {
  const access = await verifyAccess(request.headers.get('Authorization'));
  if (!access) return response.json(null, { status: 401 });

  const providerData: ProviderData = { ... };

  return response.status(200).json(providerData);
}
```

```tsx title="next.config.js"
module.exports = {
  async rewrites() {
    return [
      {
        source: '/.well-known/vercel/flags',
        destination: '/api/vercel/flags',
      },
    ];
  },
};
```

### `mergeProviderData`

Merges provider data from multiple sources. Extends the feature flags defined in code with metadata from your feature flag provider, for use with the Flags Explorer.

| Parameter | Type                                        | Description                               |
| --------- | ------------------------------------------- | ----------------------------------------- |
| `data`    | `(ProviderData \| Promise<ProviderData>)[]` | A list of provider data objects to merge. |

```ts title="app/.well-known/vercel/flags/route.ts#next"
import { getProviderData, createFlagsDiscoveryEndpoint } from 'flags/next';
import { getProviderData as getStatsigProviderData } from '@flags-sdk/statsig';
import { mergeProviderData } from 'flags';
import * as flags from '../../../../flags';

export const GET = createFlagsDiscoveryEndpoint(async (request) => {
  return mergeProviderData([
    getProviderData(flags),
    getStatsigProviderData({
      consoleApiKey: process.env.STATSIG_CONSOLE_API_KEY,
      projectId: process.env.STATSIG_PROJECT_ID,
    }),
  ]);
});
```

### `reportValue`

Reports the value of a feature flag to Vercel so it can show up in Runtime Logs and be used with Web Analytics custom server-side events. Returns `undefined`.

| Parameter | Type     | Description               |
| --------- | -------- | ------------------------- |
| `key`     | `string` | Key of the feature flag   |
| `value`   | `any`    | Value of the feature flag |

```js
import { reportValue } from 'flags';

reportValue('summer-sale', true);
```

### Encryption and decryption functions

The flags package provides multiple purpose-specific encryption and decryption functions for different types of flag data. These functions add purpose claims to prevent misuse between different data types.

#### `encryptFlagValues`

Encrypts flag values data with a purpose claim. Returns a `Promise`.

| Parameter                   | Type                       | Description                                                              |
| --------------------------- | -------------------------- | ------------------------------------------------------------------------ |
| `flagValues`                | `FlagValuesType`           | Flag values to be encrypted                                              |
| `secret` (Optional)         | `string`                   | The secret being used to encrypt. Defaults to `process.env.FLAGS_SECRET` |
| `expirationTime` (Optional) | `string \| number \| Date` | When the encrypted data should expire. Defaults to '1y' (1 year)         |

```tsx title="app/page.tsx"
import { encryptFlagValues, type FlagValuesType } from 'flags';
import { FlagValues } from 'flags/react';

async function ConfidentialFlagValues({ values }: { values: FlagValuesType }) {
  const encryptedFlagValues = await encryptFlagValues(values);
  return <FlagValues values={encryptedFlagValues} />;
}

export function Page() {
  const values = { exampleFlag: true };
  return (
    <div>
      {/* Some other content */}
      <Suspense fallback={null}>
        <ConfidentialFlagValues values={values} />
      </Suspense>
    </div>
  );
}
```

#### `decryptFlagValues`

Decrypts flag values data, ensuring the proper purpose claim. Returns a `Promise`.

| Parameter           | Type     | Description                                                                   |
| ------------------- | -------- | ----------------------------------------------------------------------------- |
| `encryptedData`     | `string` | Encrypted flag values to be decrypted                                         |
| `secret` (Optional) | `string` | The secret being used to decrypt data. Defaults to `process.env.FLAGS_SECRET` |

#### `encryptFlagDefinitions`

Encrypts flag definitions data with a purpose claim. Returns a `Promise`.

| Parameter                   | Type                       | Description                                                              |
| --------------------------- | -------------------------- | ------------------------------------------------------------------------ |
| `flagDefinitions`           | `FlagDefinitionsType`      | Flag definitions to be encrypted                                         |
| `secret` (Optional)         | `string`                   | The secret being used to encrypt. Defaults to `process.env.FLAGS_SECRET` |
| `expirationTime` (Optional) | `string \| number \| Date` | When the encrypted data should expire. Defaults to '1y' (1 year)         |

#### `decryptFlagDefinitions`

Decrypts flag definitions data, ensuring the proper purpose claim. Returns a `Promise`.

| Parameter           | Type     | Description                                                                   |
| ------------------- | -------- | ----------------------------------------------------------------------------- |
| `encryptedData`     | `string` | Encrypted flag definitions to be decrypted                                    |
| `secret` (Optional) | `string` | The secret being used to decrypt data. Defaults to `process.env.FLAGS_SECRET` |

#### `encryptOverrides`

Encrypts flag overrides data with a purpose claim. Returns a `Promise`.

| Parameter                   | Type                       | Description                                                              |
| --------------------------- | -------------------------- | ------------------------------------------------------------------------ |
| `overrides`                 | `FlagOverridesType`        | Flag overrides to be encrypted                                           |
| `secret` (Optional)         | `string`                   | The secret being used to encrypt. Defaults to `process.env.FLAGS_SECRET` |
| `expirationTime` (Optional) | `string \| number \| Date` | When the encrypted data should expire. Defaults to '1y' (1 year)         |

#### `decryptOverrides`

Decrypts flag overrides data, ensuring the proper purpose claim. Returns a `Promise`.

| Parameter           | Type     | Description                                                                   |
| ------------------- | -------- | ----------------------------------------------------------------------------- |
| `encryptedData`     | `string` | Encrypted flag overrides to be decrypted                                      |
| `secret` (Optional) | `string` | The secret being used to decrypt data. Defaults to `process.env.FLAGS_SECRET` |

The primary use case for `decryptOverrides` is decrypting data stored inside the `vercel-flag-overrides` cookie.

```tsx title="app/get-flags.ts#next"
import { FlagOverridesType, decryptOverrides } from 'flags';
import { type NextRequest } from 'next/server';
import { cookies } from 'next/headers';

async function getFlags(request: NextRequest) {
  const overrideCookie = cookies().get('vercel-flag-overrides')?.value;
  const overrides = overrideCookie
    ? await decryptOverrides(overrideCookie)
    : {};

  const flags = {
    exampleFlag: overrides?.exampleFlag ?? false,
  };

  return flags;
}
```

#### `verifyAccessProof`

Compared to `verifyAccess` which is used commonly to keep your endpoint private, `verifyAccessProof` may rarely be needed for advanced use cases.

Verifies that an access proof token is valid. Returns a `Promise` with a `true` or `false` value.

| Parameter           | Type     | Description                                                            |
| ------------------- | -------- | ---------------------------------------------------------------------- |
| `encryptedData`     | `string` | Encrypted access proof token to verify                                 |
| `secret` (Optional) | `string` | The secret used for decryption. Defaults to `process.env.FLAGS_SECRET` |

```tsx title="app/verify-access-proof.ts"
import { verifyAccessProof } from 'flags';

// Example of verifying an access proof token
const isValid = await verifyAccessProof(tokenFromRequest);
if (isValid) {
  // Handle valid token
}
```

### `safeJsonStringify`

A safe version of `JSON.stringify` that escapes the resulting output to prevent XSS attacks. Returns `string`.

| Parameter             | Type                  | Description                                                                                                                                                                                                                                                                    |
| --------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `value`               | `any`                 | A valid JSON object to convert                                                                                                                                                                                                                                                 |
| `replacer` (Optional) | `function` \| `Array` | A replacer [function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#using_a_function_as_replacer) or [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#using_an_array_as_replacer) |
| `space` (Optional)    | `string` \| `number`  | Specifies the spacing in the output                                                                                                                                                                                                                                            |

```js
import { safeJsonStringify } from 'flags';

safeJsonStringify({ markup: '<html></html>' });
// '{"markup":"\\u003chtml>\\u003c/html>"}'
```


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: flags/react
description: APIs for working with feature flags in React.
---

# flags/react



If you are using React, you can use the `FlagValues` and `FlagDefinitions` components. These abstract you from needing to manually render `script` tags.
These components handle setting the correct data attributes and escaping any data to prevent XSS.

### `FlagValues`

This component is a convenience method to render the `script` tag which is used by the Flags Explorer and Web Analytics to learn about the values your feature flags evaluated to.

Pass flag data into the `FlagValues` component with the `values` prop.

| Prop     | Type             | Description                                             |
| -------- | ---------------- | ------------------------------------------------------- |
| `values` | `FlagValuesType` | The feature flag values to expose to the Vercel Toolbar |

```tsx title="app/page.tsx"
import { FlagValues } from 'flags/react';

export function Page() {
  return (
    <div>
      {/* Some other content */}
      <FlagValues values={{ exampleFlag: true }} />
    </div>
  );
}
```

To keep your flags confidential, encrypt the input:

```tsx title="app/page.tsx"
import { encryptFlagValues, type FlagValuesType } from 'flags';
import { FlagValues } from 'flags/react';

async function ConfidentialFlagValues({ values }: { values: FlagValuesType }) {
  const encryptedFlagValues = await encryptFlagValues(values);
  return <FlagValues values={encryptedFlagValues} />;
}

export function Page() {
  const values = { exampleFlag: true };
  return (
    <div>
      {/* Some other content */}
      <Suspense fallback={null}>
        <ConfidentialFlagValues values={values} />
      </Suspense>
    </div>
  );
}
```

### `FlagDefinitions`

This component is a convenience method to render the `script` tag which is used by the Flags Explorer to learn metadata about your feature flags, like the description.

Pass flag data into the `FlagDefinitions` component with the `definitions` prop.

| Prop          | Type                  | Description                                                  |
| ------------- | --------------------- | ------------------------------------------------------------ |
| `definitions` | `FlagDefinitionsType` | The feature flag definitions to expose to the Vercel Toolbar |

```tsx title="app/page.tsx"
import { FlagDefinitions } from 'flags/react';

export function Page() {
  const flagDefinitions = {
    exampleFlag: {
      options: [{ value: false }, { value: true }],
      origin: 'https://example.com/flag/exampleFlag',
      description: 'This is an example flag.',
    },
  };
  return (
    <div>
      {/* Some other content */}
      <FlagDefinitions definitions={flagDefinitions} />
    </div>
  );
}
```

To keep your flags confidential, encrypt the input:

```tsx title="app/page.tsx"
import { encryptFlagDefinitions, type FlagDefinitionsType } from 'flags';
import { FlagDefinitions } from 'flags/react';

async function ConfidentialFlagDefinitions({
  definitions,
}: {
  definitions: FlagDefinitionsType;
}) {
  const encryptedFlagDefinitions = await encryptFlagDefinitions(definitions);
  return <FlagDefinitions definitions={encryptedFlagDefinitions} />;
}

export function Page() {
  const flagDefinitions = {
    exampleFlag: {
      options: [{ value: false }, { value: true }],
      origin: 'https://example.com/flag/exampleFlag',
      description: 'This is an example flag.',
    },
  };

  return (
    <div>
      {/* Some other content */}
      <Suspense fallback={null}>
        <ConfidentialFlagDefinitions definitions={flagDefinitions} />
      </Suspense>
    </div>
  );
}
```


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: flags/next
description: APIs for working with feature flags in Next.js.
---

# flags/next



### `flag`

**Description**: Declares a feature flag.

A feature flag declared this way will automatically respect overrides set by the Flags Explorer and integrate with Runtime Logs, Web Analytics, and more.

| Parameter                 | Type                               | Description                                                                               |
| ------------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------- |
| `key`                     | `string`                           | Key of the feature flag.                                                                  |
| `decide`                  | `function`                         | Resolves the value of the feature flag.                                                   |
| `defaultValue` (Optional) | `any`                              | Fallback value in case the `decide` function returns `undefined` or throws an error.      |
| `description` (Optional)  | `string`                           | Description of the feature flag.                                                          |
| `origin` (Optional)       | `string`                           | The URL where this feature flag can be managed.                                           |
| `options` (Optional)      | `{ label?: string, value: any }[]` | Possible values a feature flag can resolve to, which are displayed in the Flags Explorer. |
| `adapter` (Optional)      | `Adapter`                          | Supply an adapter which will implement the `decide` and `origin` functions.               |
| `identify` (Optional)     | `Adapter`                          | Provide an evaluation context which will be passed to `decide`.                           |

The `key`, `description`, `origin`, and `options` appear in the Flags Explorer.

```ts title="flags.ts#next"
import { flag } from 'flags/next';

export const showSummerSale = flag<boolean>({
  key: 'summer-sale',
  async decide() {
    return false;
  },
  origin: 'https://example.com/flags/summer-sale/',
  description: 'Show Summer Holiday Sale Banner, 20% off',
  defaultValue: false,
  options: [
    // options are not necessary for boolean flags, but we customize their labels here
    { value: false, label: 'Hide' },
    { value: true, label: 'Show' },
  ],
});
```

### `createFlagsDiscoveryEndpoint`

Creates the flags discovery endpoint, a Next.js API route handler that returns flag metadata about your app's feature flags for the Flags Explorer.

This function automatically calls `verifyAccess` to ensure the request has a valid `Authorization` header and rejects unauthorized requests with a 401 status code. It also automatically adds the `x-flags-sdk-version` response header.

| Parameter        | Type       | Description                                                                            |
| ---------------- | ---------- | -------------------------------------------------------------------------------------- |
| `getApiData`     | `Function` | An async function that returns the flag metadata.                                      |
| `options`        | `Object`   | An optional options object.                                                            |
| `options.secret` | `secret`   | The secret used to ensure valid authorization. Defaults to `process.env.FLAGS_SECRET`. |

```tsx title="app/.well-known/vercel/flags/route.ts#next"
import { getProviderData, createFlagsDiscoveryEndpoint } from 'flags/next';
import * as flags from '../../../../flags'; // your app's flags

export const GET = createFlagsDiscoveryEndpoint(async () => {
  return getProviderData(flags);
});
```

This function is for App Router only and can not be used in Pages Router. If you are using Pages Router, see [`verifyAccess`](/api-reference/core/core#verifyaccess) to implement a custom endpoint.

### `getProviderData`

**Description**: Turns flags declared using `flag` into Vercel Toolbar compatible definitions.

| Parameter | Type                   | Description                                                         |
| --------- | ---------------------- | ------------------------------------------------------------------- |
| `flags`   | `Record<string, Flag>` | A record where the values are feature flags. The keys are not used. |

Use `getProviderData` to surface the feature flags defined in code to the Flags Explorer using the Flags Explorer API endpoint.

```ts title="app/.well-known/vercel/flags/route.ts#next"
import { getProviderData, createFlagsDiscoveryEndpoint } from 'flags/next';
import * as flags from '../../../../flags';

export const GET = createFlagsDiscoveryEndpoint(async (request) => {
  return getProviderData(flags);
});
```

## Precomputation

These APIs are relevant for [precomputing feature flags](/principles/precompute).

### `precompute`

**Description**: Evaluates multiple feature flags. Returns their values encoded to a single signed string.

This call is a shorthand for calling `evaluate` and `serialize` manually.

| Parameter | Type         | Description                                                      |
| --------- | ------------ | ---------------------------------------------------------------- |
| `flags`   | `function[]` | Flags                                                            |
| `code`    | `string`     | Precomputation code generated by the original `precompute` call. |

### `evaluate`

**Description**: Evaluates multiple feature flags in a single call and returns their values.

This is the recommended way to evaluate multiple feature flags at once. Prefer it over `Promise.all([flagA(), flagB()])`: `evaluate` pre-reads headers, cookies, and overrides once for the whole batch and lets adapters that implement [`bulkDecide`](/providers/custom-adapters#bulk-evaluation) resolve a group through a single call. This reduces the number of parallel promises and leaves less room for the work to be interrupted by other microtasks. See [Bulk evaluation](/frameworks/next/bulk-evaluation) for details.

| Parameter            | Type                                            | Description                                                                                 |
| -------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `flags`              | `Flag[]` \| `Record<string, Flag>`              | An array of flags (positional results) or an object whose values are flags (keyed results). |
| `request` (Optional) | `IncomingMessage` \| `NextRequest` \| `Request` | Required outside App Router — pass it in Pages Router or routing middleware.                |

```ts
import { evaluate } from 'flags/next';
import { flagA, flagB } from './flags';

// Array form — positional results
const [a, b] = await evaluate([flagA, flagB]);

// Object form — keyed results
const { a, b } = await evaluate({ a: flagA, b: flagB });
```

### `serialize`

**Description**: Turns evaluated feature flags into their serialized representation.

| Parameter           | Type         | Description                                          |
| ------------------- | ------------ | ---------------------------------------------------- |
| `flags`             | `function[]` | Feature Flags to be serialized.                      |
| `values`            | `unknown[]`  | The value each flag declared in `flags` resolved to. |
| `secret` (Optional) | `string`     | The secret used to sign the returned representation. |

```js
import { evaluate, serialize } from 'flags/next';

const values = await evaluate(precomputeFlags);
const code = await serialize(precomputeFlags, values);
```

Note that `serialize` compresses to a tiny format, with only two bytes per feature flag and a few bytes overhead for JWS signature.

The underlying algorithm has special values for boolean values and `null`. If your feature flag can return non-boolean values, it's advised to declare them in `options` when declaring the flag using `flag`. That way this serialization can store the index of the matched option instead of its values, which further shortens the emitted.

### `getPrecomputed`

**Description**: Retrieves the value of one or multiple feature flags from the precomputation and returns them as an array.

| Parameter         | Type                     | Description                                                                                                  |
| ----------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------ |
| `flag`            | `function \| function[]` | A flag or an array of flags declared using `flag` whose values should be extracted from the precomputation . |
| `precomputeFlags` | `function[]`             | Flags used when `precompute` was called and created the precomputation code.                                 |
| `code`            | `string`                 | Precomputation code generated by the original `precompute` call.                                             |

```ts

// in your flags.ts file
import {
  getPrecomputed as getPrecomputed,
  precompute as precompute,
} from 'flags/next';

const precomputeFlags = [
  showSummerBannerFlag,
  showFreeDeliveryBannerFlag,
  countryFlag,
];

// in your proxy.ts file
const code = await precompute(precomputeFlags);

// in your page.tsx file
const [showSummerBanner, showFreeDeliveryBanner] = await getPrecomputed(
  [showSummerBannerFlag, showFreeDeliveryBannerFlag],
  precomputeFlags,
  code,
);
```

It is recommended to call the feature flag directly, for example:

```ts title="flags.ts#next"
import { flag, precompute } from 'flags/next';

const showSummerSale = flag<boolean>({
  key: 'summer-sale',
  decide: () => false,
});

const precomputeFlags = [
  showSummerSale,
  /*...*/
];

const code = await precompute(precomputeFlags);

// This will not actually invoke `showSummerSale`'s `decide` function, it will only read the result.
const sale = await showSummerSale(code, precomputeFlags);
```

### `deserialize`

**Description**: Retrieves the value of all feature flags and returns them as a record. Keys will be the `key` passed to when declaring flags using `flag`. Returns `Record<string, unknown>`.

| Parameter | Type         | Description                                                      |
| --------- | ------------ | ---------------------------------------------------------------- |
| `flags`   | `function[]` | Flags                                                            |
| `code`    | `string`     | Precomputation code generated by the original `precompute` call. |

### `generatePermutations`

**Description**: Calculates all precomputations of the options of the provided flags.

| Parameter           | Type         | Description                                                                                                         |
| ------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------- |
| `flags`             | `function[]` | Flags                                                                                                               |
| `filter` (Optional) | `function`   | This function is called with every possible precomputation of the flag's options. Return `true` to keep the option. |
| `secret` (Optional) | `string`     | The secret used to sign the generated code. Defaults to `process.env.FLAGS_SECRET`                                  |

Example usage in `generateStaticParams`:

```ts title="app/[code]/page.tsx#next"
import { generatePermutations as generatePermutations } from 'flags/next';

export async function generateStaticParams() {
  const codes = await generatePermutations(precomputeFlags);
  return codes.map((code) => ({ code }));
}
```

### `dedupe`

**Description**: Prevents duplicate work by deduplicating function calls.

Any function wrapped in `dedupe` will only ever run once for the same request within the same runtime and given the same arguments.

| Parameter | Type       | Description                      |
| --------- | ---------- | -------------------------------- |
| `fn`      | `function` | The function to be deduplicated. |

This is particularly useful with the [`identify`](/frameworks/next/evaluation-context#deduplication) function to ensure user identification only happens once per request, even when the same `identify` function is passed to multiple feature flags.

```tsx title="flags.ts#next"
import { dedupe, flag } from 'flags/next';

const identify = dedupe(({ cookies }) => {
  const userId = cookies.get('user-id')?.value;
  return { user: userId ? { id: userId } : undefined };
});

export const exampleFlag = flag({
  key: 'example',
  identify,
  decide({ entities }) {
    return entities?.user?.id === 'user1';
  },
});
```

Note that `dedupe` is not available in Pages Router.

See [Dedupe](/frameworks/next/dedupe) for the dedicated docs and [Evaluation Context](/frameworks/next/evaluation-context) and [Marketing Pages](/frameworks/next/guides/marketing-pages) for examples.


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: flags/sveltekit
description: APIs for working with feature flags in SvelteKit.
---

# flags/sveltekit



### `flag`

**Description**: Declares a feature flag

A feature flag declared this way will automatically respect overrides set by Vercel Toolbar and integrate with Runtime Logs, Web Analytics, and more.

| Parameter                | Type                               | Description                                                                           |
| ------------------------ | ---------------------------------- | ------------------------------------------------------------------------------------- |
| `key`                    | `string`                           | Key of the feature flag.                                                              |
| `decide`                 | `function`                         | Resolves the value of the feature flag.                                               |
| `description` (Optional) | `string`                           | Description of the feature flag.                                                      |
| `origin` (Optional)      | `string`                           | The URL where this feature flag can be managed.                                       |
| `options` (Optional)     | `{ label?: string, value: any }[]` | Possible values a feature flag can resolve to, which are displayed in Vercel Toolbar. |
| `identify` (Optional)    | `Adapter`                          | Provide an evaluation context which will be passed to `decide`.                       |

The `key`, `description`, `origin`, and `options` appear in Vercel Toolbar.

```ts title="flags.ts"
import { flag } from 'flags/sveltekit';

export const showSummerSale = flag<boolean>({
  key: 'summer-sale',
  async decide() {
    return false;
  },
  origin: 'https://example.com/flags/summer-sale/',
  description: 'Show Summer Holiday Sale Banner, 20% off',
  options: [
    // options are not necessary for boolean flags, but we customize their labels here
    { value: false, label: 'Hide' },
    { value: true, label: 'Show' },
  ],
});
```

### `getProviderData`

**Description**: Turns flags declared using `flag` into Vercel Toolbar compatible definitions.

| Parameter | Type                   | Description                                                         |
| --------- | ---------------------- | ------------------------------------------------------------------- |
| `flags`   | `Record<string, Flag>` | A record where the values are feature flags. The keys are not used. |

### `createHandle`

**Description**: A `handle` hook that establishes context for flags, so they have access to the event object.

| Parameter | Type                                               | Description                                                           |
| --------- | -------------------------------------------------- | --------------------------------------------------------------------- |
| `options` | `{ secret: string, flags?: Record<string, Flag> }` | The `FLAGS_SECRET` environment variable and a record of all the flags |

```ts title="src/hooks.server.ts"
import { createHandle } from 'flags/sveltekit';
import { FLAGS_SECRET } from '$env/static/private';
import * as flags from '$lib/flags';

export const handle = createHandle({ secret: FLAGS_SECRET, flags });
```

Note that when composing `createHandle` with other handlers using SvelteKit's `sequence` utility then `createHandle` should come first. Only handlers after it will be able to access feature flags.

## Precomputation

These APIs are relevant for [precomputing feature flags](/principles/precompute). See the [marketing pages guide](/frameworks/sveltekit/guides/marketing-pages) to learn how to use it in SvelteKit.

### `precompute`

**Description**: Evaluates multiple feature flags. Returns their values encoded to a single signed string.

| Parameter | Type         | Description                                                      |
| --------- | ------------ | ---------------------------------------------------------------- |
| `flags`   | `function[]` | Flags                                                            |
| `code`    | `string`     | Precomputation code generated by the original `precompute` call. |

Use this inside the API that is called from `reroute`, and when using ISR or prerendering, in Routing Middleware, too.
Use it together with `reroute`/`rewrite` to pass a user-visible URL like `/marketing` to a static variant of the page, like `/marketing/abc-123`.

### `generatePermutations`

**Description**: Calculates all precomputations of the options of the provided flags.

| Parameter           | Type         | Description                                                                                                         |
| ------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------- |
| `flags`             | `function[]` | Flags                                                                                                               |
| `filter` (Optional) | `function`   | This function is called with every possible precomputation of the flag's options. Return `true` to keep the option. |
| `secret` (Optional) | `string`     | The secret used to sign the generated code. Defaults to `$env/dynamic/private#env.FLAGS_SECRET`                     |

Use this when you're prerendering pages and therefore want to generate all combinations of flag values that a page needs at build time.

Example usage:

```ts title="src/routes/[code]/+page.server.ts"
import { generatePermutations } from 'flags/sveltekit';

export const prerender = true;

export async function entries() {
  const codes = await generatePermutations(precomputeFlags);
  return codes.map((code) => ({ code }));
}
```


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Evaluation Context
description: Segment by any criteria, using an evaluation context
---

# Evaluation Context



It is common for features to be on for some users, but off for others.
For example team members working on a new setting might need to see and
use the setting, while the rest of the team need the setting to be
hidden.

The `flag` declaration accepts an `identify`
function. The entities returned from the `identify` function
are passed as an argument to the `decide` function.

## Example

A trivial case to illustrate the concept:

```ts title="src/lib/flags.ts"
import { flag } from 'flags/sveltekit';

export const exampleFlag = flag<boolean>({
  key: 'identify-example-flag',
  identify() {
    return { user: { id: 'user1' } };
  },
  decide({ entities }) {
    return entities?.user?.id === 'user1';
  },
});
```

Having first-class support for an evaluation context allows decoupling
the identifying step from the decision making step.

## Type safety

The entities can be typed using the `flag` function.

```ts title="src/lib/flags.ts"
import { flag } from 'flags/sveltekit';

interface Entities {
  user?: { id: string };
}

export const exampleFlag = flag<boolean, Entities>({
  key: 'identify-example-flag',
  identify() {
    return { user: { id: 'user1' } };
  },
  decide({ entities }) {
    return entities?.user?.id === 'user1';
  },
});
```

## Headers and cookies

The `identify` function is called with `headers`{" "}
and `cookies` arguments, which is useful when dealing with
anonymous or authenticated users.

The arguments are normalized to a common format so the same flag can be
used in Routing Middleware and within SvelteKit server contexts (load functions, server endpoints) without having to
worry about the differences in how `headers` and{" "}
`cookies` are represented there.

```ts title="src/lib/flags.ts"
import { flag } from 'flags/sveltekit';

export const exampleFlag = flag<boolean, Entities>({
  // ...
  identify({ headers, cookies }) {
    // access to normalized headers and cookies here
    headers.get('auth');
    cookies.get('auth')?.value;
    // ...
  },
  // ...
});
```

## Deduplication

Calls to `identify` will be deduped based on the object id of the passed function. That means, in order to ensure that an `identify` function is only called once per request,
make sure to extract it to a named function and reuse it across your flags.

```ts title="src/lib/flags.ts"
import type { ReadonlyHeaders, ReadonlyRequestCookies } from 'flags';
import { flag } from 'flags/sveltekit';

interface Entities {
  visitorId?: string;
}

function identify({
  cookies,
  headers,
}: {
  cookies: ReadonlyRequestCookies;
  headers: ReadonlyHeaders;
}): Entities {
  const visitorId =
    cookies.get('visitorId')?.value ?? headers.get('x-visitorId');

  return { visitorId };
}

export const exampleFlag1 = flag<boolean, Entities>({
  key: 'exampleFlag1',
  identify,
  decide({ entities }) {
    // ...
  },
});

export const exampleFlag2 = flag<boolean, Entities>({
  key: 'exampleFlag2',
  identify,
  decide({ entities }) {
    // ...
  },
});
```

## Precomputing and targeting

The [Marketing Pages](/frameworks/sveltekit/guides/marketing-pages) example shows how to identify and target users using cookies when
precomputing pages.

### Full example

<LearnMore href="/frameworks/sveltekit/guides/marketing-pages" icon="arrow">
  See the Marketing Pages example
</LearnMore>


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Quickstart
description: Using the Flags SDK in SvelteKit
---

# Quickstart



[SvelteKit](https://svelte.dev/docs/kit/introduction) is a framework for building web applications with [Svelte](https://svelte.dev/). The Flags SDK supports SvelteKit out of the box.

<CopyPrompt text="Add Flags SDK to my existing SvelteKit app. Configure `FLAGS_SECRET`, install and wire the Vercel Toolbar for local development, create a typed flag in `src/lib/flags.ts` using `flags/sveltekit`, add the server hook with `createHandle`, use the flag from server and client code where appropriate, and run the relevant type checks or build when finished.">
  Add Flags SDK to my existing SvelteKit app. Configure `FLAGS_SECRET`, install and wire the Vercel Toolbar for local development, create a typed flag in `src/lib/flags.ts` using `flags/sveltekit`, add the server hook with `createHandle`, use the flag from server and client code where appropriate, and run the relevant type checks or build when finished.
</CopyPrompt>

A minimal feature flag declaration for SvelteKit looks like this:

```ts title="src/lib/flags.ts"
import { flag } from 'flags/sveltekit';

export const exampleFlag = flag<boolean>({
  key: 'example-flag',
  decide() {
    return false;
  },
});
```

## Installation

Install the Vercel CLI using the following command:

```sh title="Terminal"
npm i -g vercel@latest
```

## Set up SvelteKit application

1. Set up your SvelteKit application:
   ```sh title="Terminal"
   npx sv create sveltekit-flags-example
   cd sveltekit-flags-example
   npm run dev
   ```
   This will prompt you with a number of questions to create your app. Choose the following options:
   * *Choose SveleteKit minimal*
   * *Choose TypeScript*
   * *Choose Prettier*
2. At this stage the project only exists locally and not on Vercel. Use the following command to link it to project on Vercel:
   ```sh title="Terminal"
   vc link
   ```
3. Add the `FLAGS_SECRET` environment variable. Use a separate value for each environment (Development, Preview, and Production), and mark the Preview and Production values as Sensitive.

   Run this command once per environment to generate distinct secrets:

   ```sh title="Terminal"
   node -e "console.log(crypto.randomBytes(32).toString('base64url'))"
   ```

   Then store each secret as the `FLAGS_SECRET` environment variable for the matching environment:

   ```sh title="Terminal"
   vercel env add FLAGS_SECRET production --sensitive --value <production-secret>
   vercel env add FLAGS_SECRET preview --sensitive --value <preview-secret>
   vercel env add FLAGS_SECRET development --value <development-secret>
   ```
4. Finally, pull any env vars from your project on Vercel locally
   ```sh title="Terminal"
   vc env pull
   ```

## Add the toolbar locally

1. Install the `@vercel/toolbar` package:
   ```sh
   npm i @vercel/toolbar
   ```

2. In your `vite.config.ts` file add toolbar plugin for vite:

   ```ts title="vite.config.ts"
   import { sveltekit } from '@sveltejs/kit/vite';
   import { defineConfig } from 'vite';
   import { vercelToolbar } from '@vercel/toolbar/plugins/vite';

   export default defineConfig({
     plugins: [sveltekit(), vercelToolbar()],
   });
   ```

3. Next render the toolbar in your layout so that it's visible for your visitors. This renders the toolbar for all visitors. In production you may want to [render it for team members only](https://vercel.com/docs/workflow-collaboration/vercel-toolbar/in-production-and-localhost/add-to-production):

   ```html title="src/routes/+layout.svelte#svelte"
   <script lang="ts">
     import type { LayoutProps } from './$types';

     import { mountVercelToolbar } from '@vercel/toolbar/vite';
     import { onMount } from 'svelte';

     onMount(() => mountVercelToolbar());

     let { children }: LayoutProps = $props();
   </script>

   <main>
     <!-- +page.svelte is rendered in here -->
     {@render children()}
   </main>
   ```

4. Run your application locally to check that things are working:

   ```sh
   npm run dev
   ```

   You will see an error about `SvelteKitError: Not found: /.well-known/vercel/flags`. This happens because we already created the `FLAGS_SECRET` but we did not set up the flags package yet. Set it up next.

## Set up `flags`

1. Install the `flags` package:

   ```sh
   npm i flags
   ```

   If you use an AI coding assistant, we recommend installing the Flags SDK agent skill:

   ```sh
   npx skills add vercel/flags --skill flags-sdk
   ```

2. Create your first feature flag by importing the `flag` method from `flags/sveltekit`:

   ```ts title="src/lib/flags.ts"
   import { flag } from 'flags/sveltekit';

   export const showDashboard = flag<boolean>({
     key: 'showDashboard',
     description: 'Show the dashboard', // optional
     origin: 'https://example.com/#showdashbord', // optional
     options: [{ value: true }, { value: false }], // optional
     // can be async and has access to the event
     decide(_event) {
       return false;
     },
   });
   ```

3. Next set up the server hook. This is a one-time setup step which makes the toolbar aware of your application's feature flags:

   ```ts title="src/hooks.server.ts"
   import { createHandle } from 'flags/sveltekit';
   import { FLAGS_SECRET } from '$env/static/private';
   import * as flags from '$lib/flags';

   export const handle = createHandle({ secret: FLAGS_SECRET, flags });
   ```

4. You can now use this flag in code. Evaluate the flag on the server, and forward the value to the client:

   ```ts title="src/routes/+page.server.ts"
   import { showDashboard } from '$lib/flags';

   export const load = async () => {
     const dashboard = await showDashboard();

     return {
       post: {
         title: dashboard ? 'New Dashboard' : `Old Dashboard`,
       },
     };
   };
   ```

   Accessing the value on the client:

   ```html title="src/routes/+page.svelte#svelte"
   <script lang="ts">
     import type { PageProps } from './$types';

     let { data }: PageProps = $props();
   </script>

   <h1>{data.post.title}</h1>
   ```

<LearnMore href="/frameworks/sveltekit/guides/dashboard-pages" icon="arrow">
  See the Dashboard Pages guide for more info
</LearnMore>

## Flags Explorer

Open the Flags Explorer locally to see the feature flag.

<ThemeAwareImage
  src={{
  light:
    "https://assets.vercel.com/image/upload/v1740065313/flags-sdk-dev/pqlb2ony8jwm6iedf9ds.jpg",
  dark: "https://assets.vercel.com/image/upload/v1740065313/flags-sdk-dev/gbkv6rkqirktcgcc8cve.jpg",
}}
  alt="View the flag from the toolbar."
  priority
  className="border-300 rounded-lg border"
  width={1042 / 2}
  height={1197 / 2}
/>

<LearnMore icon="arrow" href="https://vercel.com/docs/flags/flags-explorer" target="_blank">
  Learn more about the Flags Explorer
</LearnMore>

### Available flags

Notice how the toolbar knows about the flag's name, description and the link to where the flag can be managed. All of these are communicated through the `/.well-known/vercel/flags` endpoint, which is set up automatically by the `createHandle` call we made in `hooks.server.ts`.

This hook intercepts all requests and responds with the application's feature flags when it sees the authenticated request made by Vercel Toolbar to load your application's feature flags.

### Overrides

When you set an override using Vercel Toolbar it will automatically be respected by the feature flags defined through `flags/sveltekit`.

### Resolved flag values

Vercel Toolbar also shows the current value of your feature flag, in this case `false`. This value could be different for each visitor, so it can not be loaded along with the information about the feature flag itself.

Instead, when a feature flag gets evaluated on the server, the hook configured in `hooks.server.ts` injects a `<script data-flag-values />` tag into the response, which contains encrypted information about the feature flag values used when generating that response. This means even if your flag would return `Math.random()` you would still be able to see the exact value used when generating the page.

## Next steps

### Precomputed flags

Precomputing flags allow experimentation on static pages, while avoiding layout shift. The Flags SDK for SvelteKit supports precomputing flags.

<LearnMore href="/frameworks/sveltekit/precompute" icon="arrow">
  Learn how to precompute values at build time
</LearnMore>

### Evaluation Contexts

Evaluation Contexts allow targeting flags to specific users. The Flags SDK for SvelteKit supports the Evaluation Context by passing an `identify` function to the flag declaration.

<LearnMore href="/frameworks/sveltekit/precompute" icon="arrow">
  Learn how to identify users with the evaluation context
</LearnMore>

### API reference

<LearnMore href="/api-reference/frameworks/sveltekit" icon="arrow">
  APIs for working with feature flags in SvelteKit
</LearnMore>


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Precompute
description: Using the precompute pattern in SvelteKit
---

# Precompute



This page shows how to implement the precompute pattern in Next.js to keep pages static, even when multiple feature flags are used on a single page, or even when feature flags are used across multiple pages.
Ensure you've read about the general precompute concept to understand the pattern and benefits:

<CopyPrompt text="Implement precomputed feature flags in my SvelteKit app. Add `FLAGS_SECRET`, group the relevant `flags/sveltekit` declarations, add server-side precompute logic, use SvelteKit `reroute` for client navigations, use Vercel Routing Middleware for full page requests, update the `[code]` route to read precomputed values, configure prerendering or ISR for variants, and run the relevant checks when finished.">
  Implement precomputed feature flags in my SvelteKit app. Add `FLAGS_SECRET`, group the relevant `flags/sveltekit` declarations, add server-side precompute logic, use SvelteKit `reroute` for client navigations, use Vercel Routing Middleware for full page requests, update the `[code]` route to read precomputed values, configure prerendering or ISR for variants, and run the relevant checks when finished.
</CopyPrompt>

<LearnMore href="/principles/precompute" icon="arrow">
  <div>
    Read the introduction to precompute
  </div>
</LearnMore>

The following assumes you've already set up the Flags SDK for SvelteKit as described in the [Quickstart guide](/frameworks/sveltekit).

## Manual approach

The most direct approach is to have a flag, evaluate it before the CDN is hit, and rewrite a path like `/pricing` to either `/pricing-variant-a` or `/pricing-variant-b`.
At a high level you use Routing Middleware and SvelteKit's [`reroute` hook](https://svelte.dev/docs/kit/hooks#Universal-hooks-reroute) to rewrite the incoming request between static versions of
the page. These static versions are hardcoded, i.e. created upfront, and rewriting to one of them happens as hardcoded logic as well.

<IframeBrowser src="sveltekit-snippets:/examples/marketing-pages-manual-approach" codeSrc="https://github.com/vercel/flags/tree/main/examples/sveltekit-example/src/routes/examples/(marketing-pages-manual-approach)" />

This approach works well for straightforward cases, but has a few downsides:

* It can be cumbersome having to maintain both `/pricing-variant-a/+page.svelte` and `/pricing-variant-b/+page.svelte`.
* It doesn't scale well when a feature flag is used on more than one page, or when multiple feature flags or flags with many variants are used on a single page.

## Why both Routing Middleware and reroute

You may wonder why we need to use both Routing Middleware and SvelteKit's [`reroute` hook](https://svelte.dev/docs/kit/hooks#Universal-hooks-reroute). In short:

* `middleware.ts` takes care of the initial full page visit
* `reroute` takes care of all subsequent client-side navigations
* `middleware.ts` will be ignored during development, as SvelteKit doesn't know about it. `reroute` will also take care of the initial full page visit in that case
* `middleware.ts` has access to cookies and private environment variables, `reroute` does not, which is why the latter needs to defer to the server to compute the result

### Reroute hook

When SvelteKit resolves a URL to a route, it does so using a route manifest that is
sent to the client on startup. Before the route is resolved, the `reroute` hook runs, which can rewrite the URL under the hood. That way
we can keep the visible URL as e.g. `/pricing` while under the hood we reroute to one of the static versions of the page, e.g. `/pricing-variant-a`.

`reroute` runs on the client, but we need to access cookies and precompute a value to know which static version of the page to load, which in turn uses private environment variables.
That means that `reroute` needs to make a request to the server to let the logic happen there.

```ts title="src/hooks.ts"
export async function reroute({ url, fetch }) {
  if (url.pathname === '/pricing') {
    const destination = new URL('/api/reroute-manual', url);

    return fetch(destination).then((response) => response.text());
  }
}
```

The server can resolve the flag value (which may use cookies or headers which may be decrypted). Depending on that a rewritten pathname is returned, which SvelteKit's route resolution logic then uses to decide which components and data to load.

```ts title="src/routes/api/reroute-manual/+server.ts"
import { exampleFlag } from '$lib/flags.js';
import { text } from '@sveltejs/kit';

export async function GET({ request, cookies }) {
  const example = await exampleFlag(request);
  return text(example ? '/pricing-variant-a' : '/pricing-variant-b');
}
```

### Routing Middleware

`reroute` is used at development time for all requests, both client and server-side. It is also called in production during soft navigations.

However when doing a full page visit (i.e. when a user first hits your page) in production
we need to run something *before* the SvelteKit runtime, as we are using ISR or prerendering. `reroute` would run as part of the SvelteKit runtime, so it's too late. For that reason,
we need to duplicate a bit of code within `proxy.ts`, which will be deployed by Vercel as Routing Middleware, which will run before the CDN is hit. Inside the middleware we use flags
to rewrite the URL to a static variant of the page, similar to what we did in the server request as part of `reroute`.

```ts title="middleware.ts"
import { rewrite } from '@vercel/edge';
import { exampleFlag } from './src/lib/flags';

export const config = {
  matcher: ['/pricing'],
};

export default async function middleware(request: Request) {
  const example = await exampleFlag(request);

    // Get destination URL based on the feature flag
  return rewrite(example ? '/pricing-variant-a' : '/pricing-variant-b');
}
```

## Precomputing

Use the precompute functionality of the Flags SDK to work around the limitations of the manual approach.
Use the precompute pattern to keep pages static, even when multiple feature flags are used on a single page, or even when feature flags are used across multiple pages.

At a high level you still use Routing Middleware and the `reroute` hook to rewrite the incoming request between static versions of
the page. The difference is that you no longer hardcode those static versions, instead you create a dynamic route segment which is then
filled with a hash that is generated from all the flag values used on that page.

### Prerequisites

Make sure you've set up the Flags SDK for SvelteKit as described in the [Quickstart guide](/docs/getting-started/sveltekit). Additionally, install the `@vercel/edge` dependency from npm.

### 1. Create flags to be precomputed

Create one or multiple flags.

```ts title="src/lib/flags.ts"
import { flag } from 'flags/sveltekit';

export const firstPricingABTest = flag({
  key: 'firstPricingABTest',
  decide: () => false,
});

export const secondPricingABTest = flag({
  key: 'secondPricingABTest',
  decide: () => false,
});
```

Export them as an array to be precomputed. Put them into a different file to later colocate other logic related to precomputing.

```ts title="src/lib/precomputed-flags.ts"
import { firstPricingABTest, secondPricingABTest } from './flags';

export const pricingFlags = [firstPricingABTest, secondPricingABTest];
```

### 2. Precompute flags from reroute hook

Set up the reroute hook to defer resolution of the pricing URL to the server.

```ts title="src/hooks.ts"
export async function reroute({ url, fetch }) {
  if (url.pathname === '/pricing') {
    const destination = new URL('/api/reroute', url);
    destination.searchParams.set('pathname', url.pathname);

    return fetch(destination).then((response) => response.text());
  }
}
```

Add the server endpoint and compute the URL that should be routed to under the hood.

```ts title="src/routes/api/reroute/+server.ts"
import { text } from '@sveltejs/kit';
import { computeInternalRoute } from '$lib/precomputed-flags';

export async function GET({ url, request, cookies, setHeaders }) {
  const destination = await computeInternalRoute(
    url.searchParams.get('pathname')!,
    request,
  );
  return text(destination);
}
```

This makes use of `computeInternalRoute`, which you add to the file where you exported the flags array from.
This is where the precomputation happens by using the `precompute` function which you pass the flags used on that page and the current request.
`precompute` will use these to invoke each flag, retrieve their value, and encode it as a route segment. As a result the user-visible URL `/pricing` is
internally rewritten to something like `/pricing/asd-qwe-123`.

```ts title="src/lib/precomputed-flags.ts"
import { precompute } from 'flags/sveltekit';

export async function computeInternalRoute(pathname: string, request: Request) {
  if (pathname === '/pricing') {
    return '/pricing/' + (await precompute(pricingFlags, request));
  }

  // You can easily enhance this function to add more precomputed routes

  return pathname;
}
```

### 3. Precompute flags from middleware

Add similar logic to Routing Middleware, reusing the shared logic from `precomputed-flags.ts`.

```ts title="middleware.ts"
import { rewrite } from '@vercel/edge';
import { normalizeUrl } from '@sveltejs/kit';
import { computeInternalRoute } from './src/lib/precomputed-flags';

export const config = {
  matcher: ['/pricing'],
};

export default async function middleware(request: Request) {
  const { url, denormalize } = normalizeUrl(request.url);

  if (url.pathname === '/pricing') {
    return rewrite(
      // Get destination URL based on the feature flag
      denormalize(await computeInternalRoute(url.pathname, request)),
    );
  }
}
```

### 4. Access the precomputation result from a page

Next, import the feature flags you created earlier while providing the code from the URL and the pricingFlags list of flags used in the precomputation.

When e.g. the `firstPricingABTest` flag is called within this server load function it reads the result from the precomputation, and it does not invoke the flag's decide function again:

```ts title="src/routes/pricing/[code]/+page.server.ts"
import type { PageServerLoad } from './$types';
import { firstPricingABTest, secondPricingABTest } from '$lib/flags';
import { pricingFlags } from '$lib/precomputed-flags';

export const load: PageServerLoad = async ({ params }) => {
  const flag1 = await firstPricingABTest(params.code, pricingFlags);
  const flag2 = await secondPricingABTest(params.code, pricingFlags);

  return {
    first: `First flag evaluated to ${flag1}`,
    second: `Second flag evaluated to ${flag2}`,
  };
};
```

```ts title="src/routes/pricing/[code]/+page.svelte"
<script>
  let { data } = $props();
</script>

<p>{data.first}</p>
<p>{data.second}</p>
```

## Enabling ISR (optional)

Now that the flags are precomputed, you should make sure to cache the result. You can do that by using Incremental Static Regeneration (ISR) on Vercel:

```ts title="src/routes/pricing/[code]/+page.server.ts"
export const config = {
  isr: {
    expiration: false,
  },
};

export const load: PageServerLoad = async ({ params }) => {
  // ...
};
```

## Enabling prerendering (optional)

You can precompute the results at build time instead of at runtime and prerender the results. For this, opt in to SvelteKit's prerendering using `export const prerender = true`.
Then use `generatePermutations` within `entries`, through which you tell SvelteKit what variants of (in this example) the `/pricing/[code]` route exist, which it will then prerender.

```ts title="src/routes/pricing/[code]/+page.server.ts"
import { pricingFlags } from '$lib/precomputed-flags';
import { generatePermutations } from 'flags/sveltekit';

export const prerender = true;

export async function entries() {
  return (await generatePermutations(pricingFlags)).map((code) => ({ code }));
}

export const load: PageServerLoad = async ({ params }) => {
  // ...
};
```

## Next steps

You now know how to use the precompute pattern within SvelteKit.
The above example didn't get into details about how to use e.g. cookies to determine the value of the flag.
This and more is covered in the Marketing Pages guide.

<LearnMore href="/frameworks/sveltekit/guides/marketing-pages" icon="arrow">
  See the Marketing Pages example which implements this pattern
</LearnMore>


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Bulk Evaluation
description: Evaluate multiple feature flags at once with evaluate().
---

# Bulk Evaluation



`evaluate` (from `flags/next`) resolves multiple feature flags in a single call.
Use it instead of awaiting flags one at a time or resolving them with
`Promise.all`, both of which add avoidable latency or overhead. To evaluate a
**single** flag, keep calling it directly with `await myFlag()`.

## Why not await flags one by one or with `Promise.all`?

Awaiting each flag in turn blocks on every flag before starting the next one, so
the flags resolve sequentially. Total latency becomes the sum of every flag's
evaluation time instead of the slowest single flag.

```ts title="example.ts"
import { flagA, flagB } from "./flags";

// avoid: each await blocks the next, so the flags resolve sequentially
const a = await flagA();
const b = await flagB();
```

`Promise.all` removes the sequential wait by starting every flag at once, but it
evaluates each flag in isolation.

```ts title="example.ts"
import { flagA, flagB } from "./flags";

// avoid: resolves in parallel, but can't share work across the flags
const [a, b] = await Promise.all([flagA(), flagB()]);
```

Because each flag runs on its own, `Promise.all` can't reuse work across the
batch. Every flag reads headers, cookies, and overrides again, and adapters
can't resolve a group of flags through a single call. It also creates one
promise per flag, with each flag spawning further internal promises as it
evaluates, which adds microtask queue overhead and leaves more room for the work
to be interrupted by other microtasks.

## Use `evaluate` instead

`evaluate` resolves a set of flags in a single call. It pre-reads headers,
cookies, and overrides once for the whole batch and lets adapters resolve a
group through one call, so it shares work across evaluations and reduces the
number of parallel promises the runtime has to manage.

```ts title="example.ts"
import { evaluate } from "flags/next";
import { flagA, flagB } from "./flags";

// prefer: shares work across the batch
const [a, b] = await evaluate([flagA, flagB]);
```

`evaluate` accepts either an **array** of flags, returning positional results,
or an **object** whose values are flags, returning keyed results.

```ts title="example.ts"
// array form — positional results
const [a, b] = await evaluate([flagA, flagB]);

// object form — keyed results
const { a, b } = await evaluate({ a: flagA, b: flagB });
```

## Evaluate outside the App Router

Outside the App Router, in Pages Router (`getServerSideProps`, API routes) or in
routing middleware, pass the request as the second argument so `evaluate` can
read headers and cookies:

```ts title="middleware.ts"
const [a, b] = await evaluate([flagA, flagB], request);
```

## Evaluation context

`evaluate` accepts only flags and an optional request. It does not take an
evaluation context or entities argument. Each flag resolves its own
[evaluation context](/frameworks/next/evaluation-context) from the `identify`
function declared on the flag, and `evaluate` calls that `identify` for you,
sharing the result across the batch.

To control the context for a flag evaluated through `evaluate`, set it in that
flag's `identify` function, which can read the request's headers and cookies and
return whatever entities you need. Passing entities directly is only supported
when calling a single flag with `await myFlag.run({ identify: entities })`,
which bypasses `identify` and uses the entities you provide.

## Evaluate vs. precomputed values

`evaluate` always evaluates flags **dynamically** at request time. It calls each flag's adapter (or `decide`), the same as calling the flag directly does.

It is **not** the way to read the values of [precomputed](/frameworks/next/precompute)
flags. When flags were precomputed in the proxy and encoded into a `code`, read
their values without re-evaluating using
[`getPrecomputed`](/api-reference/frameworks/next#getprecomputed) (or by calling
the flag with the code, `await myFlag(code, flagGroup)`).

## Adapters can batch evaluation

Aside from the Flags SDK itself getting faster, adapters can implement the
optional [`bulkDecide`](/providers/custom-adapters#bulk-evaluation) hook. When an
adapter implements it, `evaluate` calls `bulkDecide` once per group of flags that
share an adapter and `identify` source, instead of calling `decide` per flag, so
the underlying provider can share work across evaluations too.

The [Vercel adapter](/providers/vercel) (`@flags-sdk/vercel`) implements
`bulkDecide`, with roughly a 10x reduction in evaluation time when resolving
hundreds of flags in parallel.

<LearnMore href="/providers/custom-adapters#bulk-evaluation" icon="arrow">
  Implement `bulkDecide` in a custom adapter
</LearnMore>


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Dedupe
description: Prevent duplicate work by deduplicating function calls.
---

# Dedupe



Any function wrapped in `dedupe` will only ever run once for
the same request within the same runtime and given the same arguments.

The `dedupe` function is an integral piece when working with
the Flags SDK.

## Example

```tsx title="app/page.tsx"
import { dedupe } from 'flags/next';

const dedupeExample = dedupe(() => {
  return Math.random();
});

export default async function Page() {
  const random1 = await dedupeExample();
  const random2 = await dedupeExample();
  const random3 = await dedupeExample();

  // these will all be the same random number
  return (
    <div>
      {random1} {random2} {random3}
    </div>
  );
}
```

Example of output:

<IframeBrowser src="snippets:/concepts/dedupe" codeSrc="https://github.com/vercel/flags/blob/main/examples/snippets/app/concepts/dedupe/page.tsx" />

## Use cases

### Avoiding duplicate work

This helper is useful in combination with the `identify` function, as it
allows the identification to only happen once per request.
This prevents overhead when passing the same{" "}
`identify` function to multiple feature flags, or when using the same flag multiple times.

### Generating consistent random IDs

When experimenting on anonymous visitors it is common to set a cookie
containing a random id from Proxy. This random id is later
used to consistently assign users to specific groups of A/B tests.

For this use case, the function generating the random id can be wrapped
in `dedupe`. The deduplicated function is then called in Routing
Functions to produce the random id, and from a flag's{" "}
`identify` function to identify the user even on the first
page visit when no cookie is present yet.

As the function is guaranteed to generate the same id the Routing
Functions can set a cookie containing the generated id in a response,
and the feature flag can already use the generated id even if the
original request did not contain the id.

## Limitations

Note that `dedupe` is not available in Pages Router.

<LearnMore href="/frameworks/next/guides/marketing-pages" icon="arrow">
  See the Marketing Pages example
</LearnMore>


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Evaluation Context
description: Segment by any criteria, using an evaluation context.
---

# Evaluation Context



import Link from 'next/link';

It is common for features to be on for some users, but off for others.
For example team members working on a new setting might need to see and
use the setting, while the rest of the team need the setting to be
hidden.

The `flag` declaration accepts an `identify`
function. The entities returned from the `identify` function
are passed as an argument to the `decide` function.

## Example

A trivial case to illustrate the concept:

```tsx title="flags.tsx#next"
import { flag } from 'flags/next';

export const exampleFlag = flag<boolean>({
  key: 'identify-example-flag',
  identify() {
    return { user: { id: 'user1' } };
  },
  decide({ entities }) {
    return entities?.user?.id === 'user1';
  },
});
```

<IframeBrowser src="snippets:/concepts/identify/basic" codeSrc="https://github.com/vercel/flags/tree/main/examples/snippets/app/concepts/identify/basic" />

Having first-class support for an evaluation context allows decoupling
the identifying step from the decision making step.

## Type safety

The entities can be typed using the `flag` function.

```tsx title="flags.tsx#next"
import { flag } from 'flags/next';

interface Entities {
  user?: { id: string };
}

export const exampleFlag = flag<boolean, Entities>({
  key: 'identify-example-flag',
  identify() {
    return { user: { id: 'user1' } };
  },
  decide({ entities }) {
    return entities?.user?.id === 'user1';
  },
});
```

## Headers and cookies

The `identify` function is called with `headers`{" "}
and `cookies` arguments, which is useful when dealing with
anonymous or authenticated users.

The arguments are normalized to a common format so the same flag can be
used in Proxy, App Router, and Pages Router without having to
worry about the differences in how `headers` and{" "}
`cookies` are represented there.

```tsx title="flags.tsx#next"
import { flag } from 'flags/next';

export const exampleFlag = flag<boolean, Entities>({
  // ...
  identify({ headers, cookies }) {
    // access to normalized headers and cookies here
    headers.get('auth');
    cookies.get('auth')?.value;
    // ...
  },
  // ...
});
```

## Deduplication

The `dedupe` function is a helper to prevent duplicate work.

Any function wrapped in `dedupe` will only ever run once for
the same request within the same runtime and given the same arguments.

This helper is useful in combination with the{" "}
`identify` function, as it allows the identification to only
happen once per request. This prevents overhead when
passing the same `identify` function to multiple feature
flags.

<LearnMore href="/frameworks/next/dedupe" icon="arrow">
  Learn more about `dedupe`
</LearnMore>

## Precomputing and targeting

The [Marketing Pages](/docs/guides/marketing-pages) example which shows how to identify and target users using cookies when
precomputing pages.

## Custom evaluation context

While it is best practice to let the `identify` function
determine the evaluation context, it is possible to provide a custom
evaluation context.

```tsx
// pass a custom evaluation context from the call site
await exampleFlag.run({ identify: { user: { id: 'user1' } } });

// pass a custom evaluation context function from the call site
await exampleFlag.run({ identify: () => ({ user: { id: 'user1' } }) });
```

This should be used sparsely, as custom evaluation context can make
feature flags less predictable across your code base.

### Full example

The example below shows how to use the `identify` function to
display different content to different users.

<IframeBrowser src="snippets:/concepts/identify/full" codeSrc="https://github.com/vercel/flags/tree/main/examples/snippets/app/concepts/identify/full" />

The above example is implemented using this feature flag:

```tsx title="flags.tsx#next"
import type { ReadonlyRequestCookies } from 'flags';
import { dedupe, flag } from 'flags/next';

interface Entities {
  user?: { id: string };
}

const identify = dedupe(
  ({ cookies }: { cookies: ReadonlyRequestCookies }): Entities => {
    // This could read a JWT instead
    const userId = cookies.get('identify-example-user-id')?.value;
    return { user: userId ? { id: userId } : undefined };
  },
);

export const identifyExampleFlag = flag<boolean, Entities>({
  key: 'identify-example-flag',
  identify,
  decide({ entities }) {
    if (!entities?.user) return false;
    return entities.user.id === 'user1';
  },
});
```

<LearnMore href="/frameworks/next/guides/marketing-pages" icon="arrow">
  See the Marketing Pages example
</LearnMore>


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Quickstart
description: Learn how to start using the Flags SDK in your Next.js project.
---

# Quickstart



import Link from 'next/link';

Add feature flags and A/B tests to your application with the Flags SDK, a free, open-source library from the creators of Next.js.

* Works with any flag provider or custom setup.
* Compatible with App Router, Pages Router, and Middleware.

<CopyPrompt text="Add Flags SDK to my existing Next.js app. Create a `flags.ts` file with a typed feature flag using `flags/next`, use the flag in an App Router page or Pages Router page based on my app structure, add the Flags Explorer discovery endpoint at `app/.well-known/vercel/flags/route.ts`, and run the relevant type checks or build when finished.">
  Add Flags SDK to my existing Next.js app. Create a `flags.ts` file with a typed feature flag using `flags/next`, use the flag in an App Router page or Pages Router page based on my app structure, add the Flags Explorer discovery endpoint at `app/.well-known/vercel/flags/route.ts`, and run the relevant type checks or build when finished.
</CopyPrompt>

## Installation

Install the Flags SDK using your preferred package manager:

```sh title="Terminal"
npm install flags
```

If you use an AI coding assistant, we recommend installing the Flags SDK agent skill:

```sh title="Terminal"
npx skills add vercel/flags --skill flags-sdk
```

## Declaring a feature flag

Create a `flags.ts` file in your project and declare a feature flag there:

```tsx title="flags.ts#next"
import { flag } from 'flags/next';

export const exampleFlag = flag({
  key: 'example-flag',
  decide() {
    return Math.random() > 0.5;
  },
});
```

This produces an `exampleFlag` function.

## App Router

If you're using the App Router, you can call the flag function from a page, component, or middleware to evaluate the flag.

```tsx title="app/page.tsx"
import { exampleFlag } from '../flags';

export default async function Page() {
  const example = await exampleFlag();

  return <div>{example ? 'Flag is on' : 'Flag is off'}</div>;
}
```

Run `next dev` and open your browser to see the *"Flag is on"* or *"Flag is off"* text.

## Pages Router

If you're using the Pages Router, you can call the flag function inside `getServerSideProps` and pass the values to the page as props.

```tsx title="pages/index.tsx#next"
import type { InferGetServerSidePropsType, GetServerSideProps } from 'next'
import { exampleFlag } from '../flags';

export const getServerSideProps = (async ({ req }) => {
  const example = await exampleFlag(req);
  return { props: { example } };
}) satisfies GetServerSideProps<{ example: boolean }>;

export default function Page({
  example
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
  return <div>{example ? 'Flag is on' : 'Flag is off'}</div>;
}
```

Run `next dev` and open your browser to see the *"Flag is on"* or *"Flag is off"* text.

## Flags Explorer

The [Flags Explorer](https://vercel.com/docs/flags/flags-explorer) is a feature of the [Vercel Toolbar](https://vercel.com/docs/workflow-collaboration/vercel-toolbar) which allows overriding feature flags for your session only, without affecting your team members. The Flags SDK will automatically respect overrides set by the Flags Explorer.

This is useful when working with feature flags, as you can try out different states without actually changing the feature flag configuration in the flag provider.

<video width="800" height="500" controls preload="auto" className="rounded-lg shadow" muted>
  <source src="https://mxikj9vd8fb4tfe4.public.blob.vercel-storage.com/flags-explorer-demo-md-S4qQfSdVOe2JIiJ2l8BD0sA9QqjyWQ.mp4" type="video/mp4" />

  Your browser does not support the video tag.
</video>

## Next steps

<LearnMore href="/frameworks/next/precompute" icon="arrow">
  <div>
    Learn more about precompute in Next.js
  </div>
</LearnMore>

<LearnMore icon="arrow" href="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fexamples%2Ftree%2Fmain%2Fsolutions%2Fflags-sdk&env=FLAGS_SECRET&envDescription=The%20FLAGS_SECRET%20will%20be%20used%20by%20the%20Flags%20Explorer%20to%20securely%20overwrite%20feature%20flags.%20Must%20be%2032%20random%20bytes%2C%20base64-encoded.%20Use%20the%20generated%20value%20or%20set%20your%20own.&envLink=https%3A%2F%2Fvercel.com%2Fdocs%2Fworkflow-collaboration%2Ffeature-flags%2Fsupporting-feature-flags%23flags_secret-environment-variable&project-name=flags-sdk-example&repository-name=flags-sdk-example" target="_blank">
  <div>
    Clone the Flags SDK example
  </div>
</LearnMore>


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Precompute
description: Using the precompute pattern in Next.js
---

# Precompute



This page shows how to implement the precompute pattern in Next.js to keep pages static, even when multiple feature flags are used on a single page, or even when feature flags are used across multiple pages.
Ensure you've read about the general precompute concept to understand the pattern and benefits:

<CopyPrompt text="Implement precomputed feature flags in my Next.js app. Add a `FLAGS_SECRET`, group the relevant `flags/next` declarations, call `precompute` from Proxy to rewrite requests with the generated code, update the destination route to read precomputed values from `[code]`, generate static params or use ISR for variants, and run the relevant type checks or build when finished.">
  Implement precomputed feature flags in my Next.js app. Add a `FLAGS_SECRET`, group the relevant `flags/next` declarations, call `precompute` from Proxy to rewrite requests with the generated code, update the destination route to read precomputed values from `[code]`, generate static params or use ISR for variants, and run the relevant type checks or build when finished.
</CopyPrompt>

<LearnMore href="/principles/precompute" icon="arrow">
  <div>
    Read the introduction to precompute
  </div>
</LearnMore>

## Manual approach

You can manually create variants of a page by creating two versions of the same page. For example, `app/home-a/page.tsx` and `app/home-b/page.tsx`. Then, use Proxy to rewrite the request either to `/home-a` or `/home-b`.

```tsx title="flags.tsx#next"
import { flag } from 'flags/next';

export const homeFlag = flag<boolean>({
  key: 'home',
  decide: () => Math.random() > 0.5,
});
```

```tsx title="proxy.ts#next"
import { NextResponse, type NextRequest } from 'next/server';
import { homeFlag } from './flags';

export const config = { matcher: ['/'] };

export async function proxy(request: NextRequest) {
  const home = await homeFlag();

  // Determine which version to show based on the feature flag
  const version = home ? '/home-b' : '/home-a';

  // Rewrite the request to the appropriate version
  const nextUrl = new URL(version, request.url);
  return NextResponse.rewrite(nextUrl);
}
```

<IframeBrowser src="snippets:/concepts/precompute/manual" codeSrc="https://github.com/vercel/flags/tree/main/examples/snippets/app/concepts/precompute/manual" />

This approach works well for basic cases, but has a few downsides:

* It can be cumbersome having to maintain both `/home-a/page.tsx` and `/home-b/page.tsx`.
* It doesn't scale well when a feature flag is used on more than one page, or when multiple feature flags are used on a single page.

## Precompute pattern

Use the precompute functionality of the Flags SDK to work around the limitations of the manual approach. Use the precompute pattern to keep pages static, even when multiple feature flags are used on a single page, or even when feature flags are used across multiple pages.

### Prerequisites

Ensure you have a `FLAGS_SECRET` environment variable set. This variable is used by `precompute` to encrypt the precomputation result into the URL. The `FLAGS_SECRET` environment variable is required for precompute to work. It must contain a random string of 32 characters, encoded as Base64.

Use a separate `FLAGS_SECRET` value for each environment (Development, Preview, and Production), and mark the Preview and Production values as Sensitive.

Generate a random value using the following command, and run it once per environment to produce distinct values:

```sh title="Terminal"
node -e "console.log(crypto.randomBytes(32).toString('base64url'))"
```

Create the environment variable on Vercel for each environment, filling in the generated values:

```sh title="Terminal"
vercel env add FLAGS_SECRET production --sensitive --value <production-secret>
vercel env add FLAGS_SECRET preview --sensitive --value <preview-secret>
vercel env add FLAGS_SECRET development --value <development-secret>
```

Pull the environment variable to your local project:

```sh title="Terminal"
vc env pull
```

Alternatively, set the `FLAGS_SECRET` environment variable locally in `.env.local`:

```sh
FLAGS_SECRET=<your-secret>
```

### 1. Create flags to be precomputed

Export one or multiple flags as an array to be precomputed.

```tsx title="flags.tsx#next"
import { flag } from 'flags/next';

export const showSummerSale = flag({
  key: 'summer-sale',
  decide: () => false,
});

export const showBanner = flag({
  key: 'banner',
  decide: () => false,
});

// a group of feature flags to be precomputed
export const marketingFlags = [showSummerSale, showBanner] as const;
```

### 2. Precompute flags in middleware

Import and pass the group of flags to the `precompute` function in middleware. Then, forward the precomputation result (`code`) to the underlying page using an URL rewrite:

```tsx title="proxy.ts#next"
import { type NextRequest, NextResponse } from 'next/server';
import { precompute } from 'flags/next';
import { marketingFlags } from './flags';

// Note that we're running this middleware for / only, but
// you could extend it to further pages you're experimenting on
export const config = { matcher: ['/'] };

export async function proxy(request: NextRequest) {
  // precompute returns a string encoding each flag's returned value
  const code = await precompute(marketingFlags);

  // rewrites the request to include the precomputed code for this flag combination
  const nextUrl = new URL(
    `/${code}${request.nextUrl.pathname}${request.nextUrl.search}`,
    request.url,
  );

  return NextResponse.rewrite(nextUrl, { request });
}
```

### 3. Access the precomputation result from a page

Next, import the feature flags you created earlier, such as `showBanner`, while providing the code from the URL and the `marketingFlags` list of flags used in the precomputation.

When the `showBanner` flag is called within this component it reads the result from the precomputation, and it does not invoke the flag's `decide` function again:

```tsx title="app/[code]/page.tsx#next"
import { marketingFlags, showSummerSale, showBanner } from '../../flags';
type Params = Promise<{ code: string }>;

export default async function Page({ params }: { params: Params }) {
  const { code } = await params;
  // access the precomputed result by passing params.code and the group of
  // flags used during precomputation of this route segment
  const summerSale = await showSummerSale(code, marketingFlags);
  const banner = await showBanner(code, marketingFlags);

  return (
    <div>
      {banner ? <p>welcome</p> : null}

      {summerSale ? (
        <p>summer sale live now</p>
      ) : (
        <p>summer sale starting soon</p>
      )}
    </div>
  );
}
```

This approach allows middleware to decide the value of feature flags and
to pass the precomputation result down to the page. This approach also
works with API Routes.

<IframeBrowser src="snippets:/concepts/precompute/automatic" codeSrc="https://github.com/vercel/flags/tree/main/examples/snippets/app/concepts/precompute/automatic/%5Bcode%5D" />

## Enabling ISR (optional)

You can enable Incremental Static Regeneration (ISR) to cache generated pages after their initial render:

```tsx title="app/[code]/layout.tsx#next"
import type { ReactNode } from 'react';

export async function generateStaticParams() {
  // returning an empty array is enough to enable ISR
  return [];
}

export default async function Layout({ children }: { children: ReactNode }) {
  return children;
}
```

In the example above, we used [`generateStaticParams`](https://nextjs.org/docs/app/api-reference/functions/generate-static-params) on the layout. You can also specify it on the page instead. It depends on whether a single page needs the flag or all pages within that layout need the flag.

## Opting into build-time rendering (optional)

The `flags/next` submodule exposes the [`generatePermutations`](/docs/api-reference/frameworks/next#generatepermutations) helper function for generating pages for different combinations of flags at build time. This function is called and takes a list of flags and returns an array of strings representing each combination of flags:

```tsx title="app/[code]/page.tsx#next"
import type { ReactNode } from 'react';
import { generatePermutations } from 'flags/next';

export async function generateStaticParams() {
  const codes = await generatePermutations(marketingFlags);
  return codes.map((code) => ({ code }));
}

export default function Page() {
  /* ... */
}
```

You can further customize which specific combinations you want to render by passing a filter function as the second argument of `generatePermutations`. As in the example above, you can also control whether you specify these permutations on the individual pages or on a layout.

### Pages Router

If you're using the Pages Router, you need to pass a flag to `generatePermutations` which accepts the code from `context` and the group of flags.

You also need to specify a `getStaticPaths` function which can return the permutations to generate at build time or an empty array to use ISR.

```tsx title="pages/[code]/index.tsx#next"
import { generatePermutations } from 'flags/next';
import { marketingFlags, exampleFlag } from '../flags';

export const getStaticPaths = (async () => {
  const codes = await generatePermutations(marketingFlags);

  return {
    paths: codes.map((code) => ({ params: { code } })),
    fallback: 'blocking',
  };
}) satisfies GetStaticPaths;

export const getStaticProps = (async (context) => {
  if (typeof context.params?.code !== 'string') return { notFound: true };

  const example = await exampleFlag(context.params.code, marketingFlags);
  return { props: { example } };
}) satisfies GetStaticProps<{ example: boolean }>;`}
```

<IframeBrowser src="snippets:/examples/pages-router-precomputed" codeSrc="https://github.com/vercel/flags/blob/main/examples/snippets/pages/examples/pages-router-precomputed/%5Bcode%5D/index.tsx" />

## Declaring available options (optional)

Options are the possible values that a flag can take. You can declare the available options for a flag by passing an `options` array to the `flag` function:

```tsx title="flags.ts#next"
export const greetingFlag = flag<string>({
  key: 'greeting',
  options: ['Hello world', 'Hi', 'Hola'],
  decide: () => 'Hello world',
});
```

Instead of passing the values directly you can also pass an object containing a `label` and `value` property:

```tsx title="flags.ts#next"
export const greetingFlag = flag<string>({
  key: 'greeting',
  options: [
    { label: 'Hello world', value: 'Hello world' },
    { label: 'Hi', value: 'Hi' },
    { label: 'Hola', value: 'Hola' },
  ],
  decide: () => 'Hello world',
});
```

To pass objects you must specify a `label` and `value` property:

```tsx title="flags.ts#next"
export const greetingFlag = flag<string>({
  key: 'greeting',
  options: [
    {
      label: 'Hello world',
      value: {
        /* your object here */
      },
    },
  ],
});
```

The Flags SDK uses the declared options for multiple purposes:

1. Efficiently encode the flag values into the URL

   The `precompute` function generates a short code which your application then transports through the URL. The URLs must remain fairly short for the system to stay efficient. When a feature flag's `decide` function returns a value not explicitly declared in `options` the whole value needs to be inlined into the `code`, which can quickly exceed the URL size limits. For ISR in particular, the URL length needs to stay below 1024 characters.

2. Generate the possible permutations of flags

   The `generatePermutations` function generates all possible combinations of flags for prerendering at build time. The function needs to know the available options for each flag to generate the possible permutations. It can only generate and prerender options declared by the flag.

3. Show the available options in the [Flags Explorer](/docs/vercel#flags-explorer)

   All options declared for a flag are shown in the [Flags Explorer](/docs/vercel#flags-explorer). If present, the `label` is used as the option name.

## Adjusting the setup

This section shows how to adapt the precompute pattern to different scenarios.

### Precomputing a single page only

The examples above use a single top-level group of flags, which will opt all pages nested under `app/[code]` into precomputation. Instead of opting the whole application into precomputation, you can also precompute a single page only.

For example, if you want to precompute the `/pricing` page only:

* Move your pricing page from `app/pricing/page.tsx` to `app/pricing/[pricingCode]/page.tsx`
* Export a `pricingFlags` array of flags from your `flags.ts` file, containing all flags used by the pricing page
* Run Proxy for requests to `/pricing`, and pass `pricingFlags` to the `precompute` function
* Adjust the rewrite in Proxy to rewrite requests from `/pricing` to `/pricing/[pricingCode]`
* Use the `pricingFlags` array to access the precomputed result in `app/pricing/[pricingCode]/page.tsx` when using Flags

```tsx title="app/pricing/[pricingCode]/page.tsx#next"
import type { ReactNode } from 'react';
import { generatePermutations } from 'flags/next';
import { pricingFlags, discountFlag } from '../../../flags';

export async function generateStaticParams() {
  const codes = await generatePermutations(pricingFlags);
  return codes.map((code) => ({ pricingCode: code }));
}

export default async function Page(props: { params: Promise<{ pricingCode: string }> }) {
  const { pricingCode } = await props.params;
  const example = await discountFlag(pricingCode, pricingFlags);

  // ...
}
```

### Precomputing a subset of pages

This section describes an alternative folder structure where only a part of the page tree makes use of precomputation.

So far the examples have used a single top-level group of flags under `app/[code]`.

Instead, you can nest precomputed flags under a folder like `app/precomputed/[code]`. This makes it clear that only those pages will have access to the precomputed flags.

```md
app
├─page.tsx
└─precomputed
   └─[rootCode]
      └─page.tsx
```

Adjust the rewrite in Proxy to include the `precomputed` segment.

Note that you will need to manually maintain the paths in Proxy for which the rewrite should run.

### Multiple groups

Define multiple groups of flags to avoid unnecessarily generating permutations for flags which are not used by all pages. This control allows you to only generate permutations of the precise flags used by each subset of your pages.

For example, you can have a root group of flags which apply to all pages, and a nested group of flags which only apply to a single page or subset of pages. Create a root `[rootCode]` at the root of your application for the common flags, and a `[pricingCode]` for the flags used by the pricing page.

```tsx title="flags.ts#next"
// all available flags
export const navigationFlag = flag(/* ... */)
export const bannerFlag = flag(/* ... */)
export const discountFlag = flag(/* ... */)

// two groups of flags
export const rootFlags = [navigationFlag, bannerFlag];
export const pricingFlags = [discountFlag];
```

The file tree would look like this:

```md
app
└─[rootCode]
   ├─ page.tsx
   └─ pricing
       └─ [pricingCode]
            └─ page.tsx
```

To use this pattern, you need to adjust Proxy:

* Precompute both groups of flags by calling `precompute` for each group
* Adjust the rewrite to add the precomputed codes at the right segments for each group of flags

When calling the feature flag, ensure you specify the code and group of flags it was precomputed for. For example, use `await discountFlag(pricingCode, pricingFlags)` to access the precomputed value of the `discountFlag` that is part of the `pricingFlags` group.

### Combinatory explosion

Using the precompute pattern for pages which consist of many feature flags, or feature flags which have many possible values will lead to an exponential increase in the number of permutations that exist of given page.

* Build time will increase if you rely on [build time rendering](/principles/precompute#opting-into-build-time-rendering-optional).
* Cache hit rates will decrease if you rely on [lazily generating the permutations](/principles/precompute#enabling-isr-optional).

Be mindful of the flags you precompute, and whether you pregenerate permutations at build time or on demand.

You can manually specify which permutations you generate at build time by passing a second argument to `generatePermutations`. See the [API Reference](/docs/api-reference/frameworks/next#generatepermutations) for more information. All remaining permutations will be generated on demand the first time they are requested.

## Next steps

<LearnMore href="/frameworks/next/guides/marketing-pages" icon="arrow">
  <div>
    See the Marketing Pages example which implements this pattern
  </div>
</LearnMore>


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: AB Tasty
description: Use AB Tasty with the Flags SDK
---

# AB Tasty



{/*
  DO NOT EDIT THIS FILE DIRECTLY. IT IS AUTO-GENERATED.

  Use pnpm generate-openfeature-providers to generate all OpenFeature providers.
  */}

AB Tasty can be used with the Flags SDK through OpenFeature.
Set up the [Flags SDK adapter for OpenFeature](/providers/openfeature),
along with the [AB Tasty OpenFeature provider](https://github.com/flagship-io/openfeature-provider-js) to get started.

## Installation

Install the following packages:

```bash
npm install flags @flags-sdk/openfeature @flagship-io/openfeature-provider-js
```

## Setup

Set up your [AB Tasty OpenFeature provider](https://github.com/flagship-io/openfeature-provider-js) as a Flags SDK adapter.

```ts
// adapter.ts
import { OpenFeature } from "@openfeature/server-sdk";
import { createOpenFeatureAdapter } from "@flags-sdk/openfeature";
import { ABTastyProvider } from "@flagship-io/openfeature-provider-js";

export const openFeatureAdapter = createOpenFeatureAdapter(async () => {
  const provider = new ABTastyProvider("<ENV_ID>", "<API_KEY>");
  await OpenFeature.setProviderAndWait(provider);
  return OpenFeature.getClient();
});
```

See the [OpenFeature adapter](/providers/openfeature) for more details.

## Usage

Use your newly configured adapter when declaring flags.

```ts
// flags.ts
import { flag } from "flags/next";
import type { EvaluationContext } from "@openfeature/server-sdk";
import { openFeatureAdapter } from "./adapter";

export const exampleFlag = flag<boolean, EvaluationContext>({
  key: "example-flag",
  defaultValue: false,
  adapter: openFeatureAdapter.booleanValue(),
});
```

## Resources

<LearnMore icon="arrow" href="/providers/openfeature">
  Flags SDK adapter for OpenFeature
</LearnMore>

<LearnMore icon="arrow" href="https://github.com/flagship-io/openfeature-provider-js" target="_blank">
  AB Tasty OpenFeature Provider
</LearnMore>

*Note that OpenFeature providers may require additional configuration for optimal performance and compatibility. Setup instructions are provided on a best effort basis. Refer to each provider's own documentation.*

*If there is a native Flags SDK adapter for your provider, we recommend using that instead.
Native Flags SDK adapters tune your flag provider's SDK for optimal performance and integrate with Flags Explorer and Global Config.
See [available adapters](/docs/adapters/supported-providers#adapters).*

*If you are a feature flag provider interested in developing a native adapter please [open a GitHub issue](https://github.com/vercel/flags) to get in touch.*


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: CloudBees
description: Use CloudBees with the Flags SDK
---

# CloudBees



{/*
  DO NOT EDIT THIS FILE DIRECTLY. IT IS AUTO-GENERATED.

  Use pnpm generate-openfeature-providers to generate all OpenFeature providers.
  */}

CloudBees can be used with the Flags SDK through OpenFeature.
Set up the [Flags SDK adapter for OpenFeature](/providers/openfeature),
along with the [CloudBees OpenFeature provider](https://github.com/rollout/cloudbees-openfeature-provider-node) to get started.

## Installation

Install the following packages:

```bash
npm install flags @flags-sdk/openfeature cloudbees-openfeature-provider-node
```

## Setup

Set up your [CloudBees OpenFeature provider](https://github.com/rollout/cloudbees-openfeature-provider-node) as a Flags SDK adapter.

```ts
// adapter.ts
import { OpenFeature } from "@openfeature/server-sdk";
import { createOpenFeatureAdapter } from "@flags-sdk/openfeature";
import { CloudbeesProvider } from "cloudbees-openfeature-provider-node";

export const openFeatureAdapter = createOpenFeatureAdapter(async () => {
  const provider = await CloudbeesProvider.build("INSERT_APP_KEY_HERE");
  await OpenFeature.setProviderAndWait(provider);
  return OpenFeature.getClient();
});
```

See the [OpenFeature adapter](/providers/openfeature) for more details.

## Usage

Use your newly configured adapter when declaring flags.

```ts
// flags.ts
import { flag } from "flags/next";
import type { EvaluationContext } from "@openfeature/server-sdk";
import { openFeatureAdapter } from "./adapter";

export const exampleFlag = flag<boolean, EvaluationContext>({
  key: "example-flag",
  defaultValue: false,
  adapter: openFeatureAdapter.booleanValue(),
});
```

## Resources

<LearnMore icon="arrow" href="/providers/openfeature">
  Flags SDK adapter for OpenFeature
</LearnMore>

<LearnMore icon="arrow" href="https://github.com/rollout/cloudbees-openfeature-provider-node" target="_blank">
  CloudBees OpenFeature Provider
</LearnMore>

*Note that OpenFeature providers may require additional configuration for optimal performance and compatibility. Setup instructions are provided on a best effort basis. Refer to each provider's own documentation.*

*If there is a native Flags SDK adapter for your provider, we recommend using that instead.
Native Flags SDK adapters tune your flag provider's SDK for optimal performance and integrate with Flags Explorer and Global Config.
See [available adapters](/docs/adapters/supported-providers#adapters).*

*If you are a feature flag provider interested in developing a native adapter please [open a GitHub issue](https://github.com/vercel/flags) to get in touch.*


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Confidence by Spotify
description: Use Confidence by Spotify with the Flags SDK
---

# Confidence by Spotify



{/*
  DO NOT EDIT THIS FILE DIRECTLY. IT IS AUTO-GENERATED.

  Use pnpm generate-openfeature-providers to generate all OpenFeature providers.
  */}

Confidence by Spotify can be used with the Flags SDK through OpenFeature.
Set up the [Flags SDK adapter for OpenFeature](/providers/openfeature),
along with the [Confidence by Spotify OpenFeature provider](https://github.com/spotify/confidence-sdk-js) to get started.

## Installation

Install the following packages:

```bash
npm install flags @flags-sdk/openfeature @spotify-confidence/openfeature-server-provider
```

## Setup

Set up your [Confidence by Spotify OpenFeature provider](https://github.com/spotify/confidence-sdk-js) as a Flags SDK adapter.

```ts
// adapter.ts
import { OpenFeature } from "@openfeature/server-sdk";
import { createOpenFeatureAdapter } from "@flags-sdk/openfeature";
import { createConfidenceServerProvider } from '@spotify-confidence/openfeature-server-provider';

const provider = createConfidenceServerProvider({
  clientSecret: 'your-client-secret',
  fetchImplementation: fetch,
  timeout: 1000,
});

OpenFeature.setProvider(provider);
export const openFeatureAdapter = createOpenFeatureAdapter(OpenFeature.getClient());
```

See the [OpenFeature adapter](/providers/openfeature) for more details.

## Usage

Use your newly configured adapter when declaring flags.

```ts
// flags.ts
import { flag } from "flags/next";
import type { EvaluationContext } from "@openfeature/server-sdk";
import { openFeatureAdapter } from "./adapter";

export const exampleFlag = flag<boolean, EvaluationContext>({
  key: "example-flag",
  defaultValue: false,
  adapter: openFeatureAdapter.booleanValue(),
});
```

## Resources

<LearnMore icon="arrow" href="/providers/openfeature">
  Flags SDK adapter for OpenFeature
</LearnMore>

<LearnMore icon="arrow" href="https://github.com/spotify/confidence-sdk-js" target="_blank">
  Confidence by Spotify OpenFeature Provider
</LearnMore>

*Note that OpenFeature providers may require additional configuration for optimal performance and compatibility. Setup instructions are provided on a best effort basis. Refer to each provider's own documentation.*

*If there is a native Flags SDK adapter for your provider, we recommend using that instead.
Native Flags SDK adapters tune your flag provider's SDK for optimal performance and integrate with Flags Explorer and Global Config.
See [available adapters](/docs/adapters/supported-providers#adapters).*

*If you are a feature flag provider interested in developing a native adapter please [open a GitHub issue](https://github.com/vercel/flags) to get in touch.*


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: ConfigCat
description: Use ConfigCat with the Flags SDK
---

# ConfigCat



{/*
  DO NOT EDIT THIS FILE DIRECTLY. IT IS AUTO-GENERATED.

  Use pnpm generate-openfeature-providers to generate all OpenFeature providers.
  */}

ConfigCat can be used with the Flags SDK through OpenFeature.
Set up the [Flags SDK adapter for OpenFeature](/providers/openfeature),
along with the [ConfigCat OpenFeature provider](https://github.com/open-feature/js-sdk-contrib/tree/main/libs/providers/config-cat) to get started.

## Installation

Install the following packages:

```bash
npm install flags @flags-sdk/openfeature @openfeature/config-cat-provider
```

## Setup

Set up your [ConfigCat OpenFeature provider](https://github.com/open-feature/js-sdk-contrib/tree/main/libs/providers/config-cat) as a Flags SDK adapter.

```ts
// adapter.ts
import { OpenFeature } from "@openfeature/server-sdk";
import { createOpenFeatureAdapter } from "@flags-sdk/openfeature";
import { ConfigCatProvider } from '@openfeature/config-cat-provider';

export const openFeatureAdapter = createOpenFeatureAdapter(async () => {
  const provider = ConfigCatProvider.create('<sdk_key>');
  await OpenFeature.setProviderAndWait(provider);
  return OpenFeature.getClient();
});
```

See the [OpenFeature adapter](/providers/openfeature) for more details.

## Usage

Use your newly configured adapter when declaring flags.

```ts
// flags.ts
import { flag } from "flags/next";
import type { EvaluationContext } from "@openfeature/server-sdk";
import { openFeatureAdapter } from "./adapter";

export const exampleFlag = flag<boolean, EvaluationContext>({
  key: "example-flag",
  defaultValue: false,
  adapter: openFeatureAdapter.booleanValue(),
});
```

## Resources

<LearnMore icon="arrow" href="/providers/openfeature">
  Flags SDK adapter for OpenFeature
</LearnMore>

<LearnMore icon="arrow" href="https://github.com/open-feature/js-sdk-contrib/tree/main/libs/providers/config-cat" target="_blank">
  ConfigCat OpenFeature Provider
</LearnMore>

*Note that OpenFeature providers may require additional configuration for optimal performance and compatibility. Setup instructions are provided on a best effort basis. Refer to each provider's own documentation.*

*If there is a native Flags SDK adapter for your provider, we recommend using that instead.
Native Flags SDK adapters tune your flag provider's SDK for optimal performance and integrate with Flags Explorer and Global Config.
See [available adapters](/docs/adapters/supported-providers#adapters).*

*If you are a feature flag provider interested in developing a native adapter please [open a GitHub issue](https://github.com/vercel/flags) to get in touch.*


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: DevCycle
description: Use DevCycle with the Flags SDK
---

# DevCycle



{/*
  DO NOT EDIT THIS FILE DIRECTLY. IT IS AUTO-GENERATED.

  Use pnpm generate-openfeature-providers to generate all OpenFeature providers.
  */}

DevCycle can be used with the Flags SDK through OpenFeature.
Set up the [Flags SDK adapter for OpenFeature](/providers/openfeature),
along with the [DevCycle OpenFeature provider](https://docs.devcycle.com/sdk/server-side-sdks/node/node-openfeature) to get started.

## Installation

Install the following packages:

```bash
npm install flags @flags-sdk/openfeature @devcycle/nodejs-server-sdk
```

## Setup

Set up your [DevCycle OpenFeature provider](https://docs.devcycle.com/sdk/server-side-sdks/node/node-openfeature) as a Flags SDK adapter.

```ts
// adapter.ts
import { OpenFeature } from "@openfeature/server-sdk";
import { createOpenFeatureAdapter } from "@flags-sdk/openfeature";
import { DevCycleProvider } from '@devcycle/nodejs-server-sdk';

export const openFeatureAdapter = createOpenFeatureAdapter(async () => {
  const provider = new DevCycleProvider(process.env.DEVCYCLE_SERVER_SDK_KEY);
  await OpenFeature.setProviderAndWait(provider);
  return OpenFeature.getClient();
});
```

See the [OpenFeature adapter](/providers/openfeature) for more details.

## Usage

Use your newly configured adapter when declaring flags.

```ts
// flags.ts
import { flag } from "flags/next";
import type { EvaluationContext } from "@openfeature/server-sdk";
import { openFeatureAdapter } from "./adapter";

export const exampleFlag = flag<boolean, EvaluationContext>({
  key: "example-flag",
  defaultValue: false,
  adapter: openFeatureAdapter.booleanValue(),
});
```

## Resources

<LearnMore icon="arrow" href="/providers/openfeature">
  Flags SDK adapter for OpenFeature
</LearnMore>

<LearnMore icon="arrow" href="https://docs.devcycle.com/sdk/server-side-sdks/node/node-openfeature" target="_blank">
  DevCycle OpenFeature Provider
</LearnMore>

*Note that OpenFeature providers may require additional configuration for optimal performance and compatibility. Setup instructions are provided on a best effort basis. Refer to each provider's own documentation.*

*If there is a native Flags SDK adapter for your provider, we recommend using that instead.
Native Flags SDK adapters tune your flag provider's SDK for optimal performance and integrate with Flags Explorer and Global Config.
See [available adapters](/docs/adapters/supported-providers#adapters).*

*If you are a feature flag provider interested in developing a native adapter please [open a GitHub issue](https://github.com/vercel/flags) to get in touch.*


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: FeatBit
description: Use FeatBit with the Flags SDK
---

# FeatBit



{/*
  DO NOT EDIT THIS FILE DIRECTLY. IT IS AUTO-GENERATED.

  Use pnpm generate-openfeature-providers to generate all OpenFeature providers.
  */}

FeatBit can be used with the Flags SDK through OpenFeature.
Set up the [Flags SDK adapter for OpenFeature](/providers/openfeature),
along with the [FeatBit OpenFeature provider](https://github.com/featbit/openfeature-provider-node-server) to get started.

## Installation

Install the following packages:

```bash
npm install flags @flags-sdk/openfeature @featbit/node-server-sdk @featbit/openfeature-provider-node-server
```

## Setup

Set up your [FeatBit OpenFeature provider](https://github.com/featbit/openfeature-provider-node-server) as a Flags SDK adapter.

```ts
// adapter.ts
import { OpenFeature } from "@openfeature/server-sdk";
import { createOpenFeatureAdapter } from "@flags-sdk/openfeature";
import { FbProvider } from '@featbit/openfeature-provider-node-server';

export const openFeatureAdapter = createOpenFeatureAdapter(async () => {
  const provider = new FbProvider({
    sdkKey: '<your-sdk-key>',
    streamingUri: '<your-streaming-uri>',
    eventsUri: '<your-events-uri>'
  });
  await OpenFeature.setProviderAndWait(provider);
  return OpenFeature.getClient();
});
```

See the [OpenFeature adapter](/providers/openfeature) for more details.

## Usage

Use your newly configured adapter when declaring flags.

```ts
// flags.ts
import { flag } from "flags/next";
import type { EvaluationContext } from "@openfeature/server-sdk";
import { openFeatureAdapter } from "./adapter";

export const exampleFlag = flag<boolean, EvaluationContext>({
  key: "example-flag",
  defaultValue: false,
  adapter: openFeatureAdapter.booleanValue(),
});
```

## Resources

<LearnMore icon="arrow" href="/providers/openfeature">
  Flags SDK adapter for OpenFeature
</LearnMore>

<LearnMore icon="arrow" href="https://github.com/featbit/openfeature-provider-node-server" target="_blank">
  FeatBit OpenFeature Provider
</LearnMore>

*Note that OpenFeature providers may require additional configuration for optimal performance and compatibility. Setup instructions are provided on a best effort basis. Refer to each provider's own documentation.*

*If there is a native Flags SDK adapter for your provider, we recommend using that instead.
Native Flags SDK adapters tune your flag provider's SDK for optimal performance and integrate with Flags Explorer and Global Config.
See [available adapters](/docs/adapters/supported-providers#adapters).*

*If you are a feature flag provider interested in developing a native adapter please [open a GitHub issue](https://github.com/vercel/flags) to get in touch.*


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: flagd
description: Use flagd with the Flags SDK
---

# flagd



{/*
  DO NOT EDIT THIS FILE DIRECTLY. IT IS AUTO-GENERATED.

  Use pnpm generate-openfeature-providers to generate all OpenFeature providers.
  */}

flagd can be used with the Flags SDK through OpenFeature.
Set up the [Flags SDK adapter for OpenFeature](/providers/openfeature),
along with the [flagd OpenFeature provider](https://github.com/open-feature/js-sdk-contrib/tree/main/libs/providers/flagd) to get started.

## Installation

Install the following packages:

```bash
npm install flags @flags-sdk/openfeature @openfeature/flagd-provider
```

## Setup

Set up your [flagd OpenFeature provider](https://github.com/open-feature/js-sdk-contrib/tree/main/libs/providers/flagd) as a Flags SDK adapter.

```ts
// adapter.ts
import { OpenFeature } from "@openfeature/server-sdk";
import { createOpenFeatureAdapter } from "@flags-sdk/openfeature";
import { FlagdProvider } from '@openfeature/flagd-provider';

export const openFeatureAdapter = createOpenFeatureAdapter(async () => {
  const provider = new FlagdProvider(/* options */);
  await OpenFeature.setProviderAndWait(provider);
  return OpenFeature.getClient();
});
```

See the [OpenFeature adapter](/providers/openfeature) for more details.

## Usage

Use your newly configured adapter when declaring flags.

```ts
// flags.ts
import { flag } from "flags/next";
import type { EvaluationContext } from "@openfeature/server-sdk";
import { openFeatureAdapter } from "./adapter";

export const exampleFlag = flag<boolean, EvaluationContext>({
  key: "example-flag",
  defaultValue: false,
  adapter: openFeatureAdapter.booleanValue(),
});
```

## Resources

<LearnMore icon="arrow" href="/providers/openfeature">
  Flags SDK adapter for OpenFeature
</LearnMore>

<LearnMore icon="arrow" href="https://github.com/open-feature/js-sdk-contrib/tree/main/libs/providers/flagd" target="_blank">
  flagd OpenFeature Provider
</LearnMore>

*Note that OpenFeature providers may require additional configuration for optimal performance and compatibility. Setup instructions are provided on a best effort basis. Refer to each provider's own documentation.*

*If there is a native Flags SDK adapter for your provider, we recommend using that instead.
Native Flags SDK adapters tune your flag provider's SDK for optimal performance and integrate with Flags Explorer and Global Config.
See [available adapters](/docs/adapters/supported-providers#adapters).*

*If you are a feature flag provider interested in developing a native adapter please [open a GitHub issue](https://github.com/vercel/flags) to get in touch.*


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Flipt
description: Use Flipt with the Flags SDK
---

# Flipt



{/*
  DO NOT EDIT THIS FILE DIRECTLY. IT IS AUTO-GENERATED.

  Use pnpm generate-openfeature-providers to generate all OpenFeature providers.
  */}

Flipt can be used with the Flags SDK through OpenFeature.
Set up the [Flags SDK adapter for OpenFeature](/providers/openfeature),
along with the [Flipt OpenFeature provider](https://github.com/open-feature/js-sdk-contrib/tree/main/libs/providers/flipt) to get started.

## Installation

Install the following packages:

```bash
npm install flags @flags-sdk/openfeature @openfeature/flipt-provider
```

## Setup

Set up your [Flipt OpenFeature provider](https://github.com/open-feature/js-sdk-contrib/tree/main/libs/providers/flipt) as a Flags SDK adapter.

```ts
// adapter.ts
import { OpenFeature } from "@openfeature/server-sdk";
import { createOpenFeatureAdapter } from "@flags-sdk/openfeature";
import { FliptProvider } from '@openfeature/flipt-provider';

export const openFeatureAdapter = createOpenFeatureAdapter(async () => {
  const provider = new FliptProvider(
    'namespace-of-choice',
    { url: 'http://your.upstream.flipt.host' }
  );
  await OpenFeature.setProviderAndWait(provider);
  return OpenFeature.getClient();
});
```

See the [OpenFeature adapter](/providers/openfeature) for more details.

## Usage

Use your newly configured adapter when declaring flags.

```ts
// flags.ts
import { flag } from "flags/next";
import type { EvaluationContext } from "@openfeature/server-sdk";
import { openFeatureAdapter } from "./adapter";

export const exampleFlag = flag<boolean, EvaluationContext>({
  key: "example-flag",
  defaultValue: false,
  adapter: openFeatureAdapter.booleanValue(),
});
```

## Resources

<LearnMore icon="arrow" href="/providers/openfeature">
  Flags SDK adapter for OpenFeature
</LearnMore>

<LearnMore icon="arrow" href="https://github.com/open-feature/js-sdk-contrib/tree/main/libs/providers/flipt" target="_blank">
  Flipt OpenFeature Provider
</LearnMore>

*Note that OpenFeature providers may require additional configuration for optimal performance and compatibility. Setup instructions are provided on a best effort basis. Refer to each provider's own documentation.*

*If there is a native Flags SDK adapter for your provider, we recommend using that instead.
Native Flags SDK adapters tune your flag provider's SDK for optimal performance and integrate with Flags Explorer and Global Config.
See [available adapters](/docs/adapters/supported-providers#adapters).*

*If you are a feature flag provider interested in developing a native adapter please [open a GitHub issue](https://github.com/vercel/flags) to get in touch.*


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: GO FeatureFlag
description: Use GO FeatureFlag with the Flags SDK
---

# GO FeatureFlag



{/*
  DO NOT EDIT THIS FILE DIRECTLY. IT IS AUTO-GENERATED.

  Use pnpm generate-openfeature-providers to generate all OpenFeature providers.
  */}

GO FeatureFlag can be used with the Flags SDK through OpenFeature.
Set up the [Flags SDK adapter for OpenFeature](/providers/openfeature),
along with the [GO FeatureFlag OpenFeature provider](https://gofeatureflag.org/docs/sdk/server_providers/openfeature_javascript) to get started.

## Installation

Install the following packages:

```bash
npm install flags @flags-sdk/openfeature @openfeature/go-feature-flag-provider
```

## Setup

Set up your [GO FeatureFlag OpenFeature provider](https://gofeatureflag.org/docs/sdk/server_providers/openfeature_javascript) as a Flags SDK adapter.

```ts
// adapter.ts
import { OpenFeature } from "@openfeature/server-sdk";
import { createOpenFeatureAdapter } from "@flags-sdk/openfeature";
import { GoFeatureFlagProvider } from  "@openfeature/go-feature-flag-provider";

const provider = new GoFeatureFlagProvider({
  endpoint: 'http://localhost:1031/'
});

OpenFeature.setProvider(provider);
export const openFeatureAdapter = createOpenFeatureAdapter(OpenFeature.getClient());
```

See the [OpenFeature adapter](/providers/openfeature) for more details.

## Usage

Use your newly configured adapter when declaring flags.

```ts
// flags.ts
import { flag } from "flags/next";
import type { EvaluationContext } from "@openfeature/server-sdk";
import { openFeatureAdapter } from "./adapter";

export const exampleFlag = flag<boolean, EvaluationContext>({
  key: "example-flag",
  defaultValue: false,
  adapter: openFeatureAdapter.booleanValue(),
});
```

## Resources

<LearnMore icon="arrow" href="/providers/openfeature">
  Flags SDK adapter for OpenFeature
</LearnMore>

<LearnMore icon="arrow" href="https://gofeatureflag.org/docs/sdk/server_providers/openfeature_javascript" target="_blank">
  GO FeatureFlag OpenFeature Provider
</LearnMore>

*Note that OpenFeature providers may require additional configuration for optimal performance and compatibility. Setup instructions are provided on a best effort basis. Refer to each provider's own documentation.*

*If there is a native Flags SDK adapter for your provider, we recommend using that instead.
Native Flags SDK adapters tune your flag provider's SDK for optimal performance and integrate with Flags Explorer and Global Config.
See [available adapters](/docs/adapters/supported-providers#adapters).*

*If you are a feature flag provider interested in developing a native adapter please [open a GitHub issue](https://github.com/vercel/flags) to get in touch.*


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: GrowthBook
description: Use GrowthBook with the Flags SDK
---

# GrowthBook



{/*
  DO NOT EDIT THIS FILE DIRECTLY. IT IS AUTO-GENERATED.

  Use pnpm generate-openfeature-providers to generate all OpenFeature providers.
  */}

GrowthBook can be used with the Flags SDK through OpenFeature.
Set up the [Flags SDK adapter for OpenFeature](/providers/openfeature),
along with the [GrowthBook OpenFeature provider](https://github.com/open-feature/js-sdk-contrib/blob/main/libs/providers/growthbook/README.md) to get started.

***This provider has a native adapter available at [@flags-sdk/growthbook](/providers/growthbook), which you should use instead***.

## Installation

Install the following packages:

```bash
npm install flags @flags-sdk/openfeature @growthbook/growthbook @openfeature/growthbook-provider
```

## Setup

Set up your [GrowthBook OpenFeature provider](https://github.com/open-feature/js-sdk-contrib/blob/main/libs/providers/growthbook/README.md) as a Flags SDK adapter.

```ts
// adapter.ts
import { OpenFeature } from "@openfeature/server-sdk";
import { createOpenFeatureAdapter } from "@flags-sdk/openfeature";
import { GrowthBookClient, ClientOptions, InitOptions } from '@growthbook/growthbook';
import { GrowthbookProvider } from '@openfeature/growthbook-provider';

export const openFeatureAdapter = createOpenFeatureAdapter(async () => {
  /*
  * Configure your GrowthBook instance with GrowthBook context
  * @see https://docs.growthbook.io/lib/js#step-1-configure-your-app
  */
  const gbClientOptions: ClientOptions = {
    apiHost: 'https://cdn.growthbook.io',
    clientKey: 'sdk-abc123',
    // Only required if you have feature encryption enabled in GrowthBook
    decryptionKey: 'key_abc123',
  };

  /*
  * optional init options
  * @see https://docs.growthbook.io/lib/js#switching-to-init
  */
  const initOptions: InitOptions = {
    timeout: 2000,
    streaming: true,
  };

  const provider = new GrowthbookProvider(gbClientOptions, initOptions);

  await OpenFeature.setProviderAndWait(provider);
  return OpenFeature.getClient();
});
```

See the [OpenFeature adapter](/providers/openfeature) for more details.

## Usage

Use your newly configured adapter when declaring flags.

```ts
// flags.ts
import { flag } from "flags/next";
import type { EvaluationContext } from "@openfeature/server-sdk";
import { openFeatureAdapter } from "./adapter";

export const exampleFlag = flag<boolean, EvaluationContext>({
  key: "example-flag",
  defaultValue: false,
  adapter: openFeatureAdapter.booleanValue(),
});
```

## Resources

<LearnMore icon="arrow" href="/providers/openfeature">
  Flags SDK adapter for OpenFeature
</LearnMore>

<LearnMore icon="arrow" href="https://github.com/open-feature/js-sdk-contrib/blob/main/libs/providers/growthbook/README.md" target="_blank">
  GrowthBook OpenFeature Provider
</LearnMore>

*Note that OpenFeature providers may require additional configuration for optimal performance and compatibility. Setup instructions are provided on a best effort basis. Refer to each provider's own documentation.*

*If there is a native Flags SDK adapter for your provider, we recommend using that instead.
Native Flags SDK adapters tune your flag provider's SDK for optimal performance and integrate with Flags Explorer and Global Config.
See [available adapters](/docs/adapters/supported-providers#adapters).*

*If you are a feature flag provider interested in developing a native adapter please [open a GitHub issue](https://github.com/vercel/flags) to get in touch.*


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Hypertune
description: Use Hypertune with the Flags SDK
---

# Hypertune



{/*
  DO NOT EDIT THIS FILE DIRECTLY. IT IS AUTO-GENERATED.

  Use pnpm generate-openfeature-providers to generate all OpenFeature providers.
  */}

Hypertune can be used with the Flags SDK through OpenFeature.
Set up the [Flags SDK adapter for OpenFeature](/providers/openfeature),
along with the [Hypertune OpenFeature provider](https://www.npmjs.com/package/@hypertune/openfeature-server-provider) to get started.

***This provider has a native adapter available at [@flags-sdk/hypertune](/providers/hypertune), which you should use instead***.

## Installation

Install the following packages:

```bash
npm install flags @flags-sdk/openfeature @hypertune/openfeature-server-provider
```

## Setup

Set up your [Hypertune OpenFeature provider](https://www.npmjs.com/package/@hypertune/openfeature-server-provider) as a Flags SDK adapter.

```ts
// adapter.ts
import { OpenFeature } from "@openfeature/server-sdk";
import { createOpenFeatureAdapter } from "@flags-sdk/openfeature";
import { HypertuneProvider } from "@hypertune/openfeature-server-provider";

export const openFeatureAdapter = createOpenFeatureAdapter(async () => {
  const provider = new HypertuneProvider({ token: "YOUR_HYPERTUNE_TOKEN" });
  await OpenFeature.setProviderAndWait(provider);
  return OpenFeature.getClient();
});
```

See the [OpenFeature adapter](/providers/openfeature) for more details.

## Usage

Use your newly configured adapter when declaring flags.

```ts
// flags.ts
import { flag } from "flags/next";
import type { EvaluationContext } from "@openfeature/server-sdk";
import { openFeatureAdapter } from "./adapter";

export const exampleFlag = flag<boolean, EvaluationContext>({
  key: "example-flag",
  defaultValue: false,
  adapter: openFeatureAdapter.booleanValue(),
});
```

## Resources

<LearnMore icon="arrow" href="/providers/openfeature">
  Flags SDK adapter for OpenFeature
</LearnMore>

<LearnMore icon="arrow" href="https://www.npmjs.com/package/@hypertune/openfeature-server-provider" target="_blank">
  Hypertune OpenFeature Provider
</LearnMore>

*Note that OpenFeature providers may require additional configuration for optimal performance and compatibility. Setup instructions are provided on a best effort basis. Refer to each provider's own documentation.*

*If there is a native Flags SDK adapter for your provider, we recommend using that instead.
Native Flags SDK adapters tune your flag provider's SDK for optimal performance and integrate with Flags Explorer and Global Config.
See [available adapters](/docs/adapters/supported-providers#adapters).*

*If you are a feature flag provider interested in developing a native adapter please [open a GitHub issue](https://github.com/vercel/flags) to get in touch.*


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: OpenFeature
---

# OpenFeature



[OpenFeature](https://openfeature.dev/) is an open specification that provides a vendor-agnostic, community-driven API for feature flagging that works with your favorite feature flag management tool or in-house solution. The Flags SDK OpenFeature adapter allows you to use the Flags SDK with any OpenFeature provider.

<CopyPrompt text="Integrate my OpenFeature provider with Flags SDK. Install `@flags-sdk/openfeature` and `@openfeature/server-sdk`, configure my OpenFeature provider and client, create a `createOpenFeatureAdapter` instance, add a request-aware `identify` function for the evaluation context, declare typed flags with default values, add Flags Explorer support if applicable, and run the relevant type checks or build when finished.">
  Integrate my OpenFeature provider with Flags SDK. Install `@flags-sdk/openfeature` and `@openfeature/server-sdk`, configure my OpenFeature provider and client, create a `createOpenFeatureAdapter` instance, add a request-aware `identify` function for the evaluation context, declare typed flags with default values, add Flags Explorer support if applicable, and run the relevant type checks or build when finished.
</CopyPrompt>

*If there is a native Flags SDK adapter for your provider, we recommend using that instead. Native Flags SDK adapters finely tune the SDK for your flag provider, optionally integrate with Global Config and Flags Explorer, and are configured for Edge Runtime compatibility where possible. See [available adapters](/docs/adapters/supported-providers#adapters)*

The `@flags-sdk/openfeature` package provides an [adapter](#provider-instance) for loading flags from OpenFeature.

<LearnMore icon="arrow" href="/providers">
  Learn more about Adapters
</LearnMore>

<LearnMore icon="arrow" href="https://vercel.com/templates/next.js/flags-sdk-openfeature" target="_blank">
  Clone the OpenFeature template
</LearnMore>

***

## Setup

The OpenFeature provider is available in the `@flags-sdk/openfeature` module. Install it with

```bash
npm i @flags-sdk/openfeature @openfeature/server-sdk
```

The command also installs the `@openfeature/server-sdk` peer dependency, as the OpenFeature adapter depends on the [OpenFeature Node.js SDK](https://openfeature.dev/docs/reference/technologies/server/javascript/).

***

## Provider Instance

Import the `createOpenFeatureAdapter` function from `@flags-sdk/openfeature` and create an adapter instance with your OpenFeature client.

For usage with regular providers, pass the client directly:

```ts
import { createOpenFeatureAdapter } from "@flags-sdk/openfeature"

OpenFeature.setProvider(new YourProviderOfChoice())
const openFeatureAdapter = createOpenFeatureAdapter(OpenFeature.getClient());
```

For usage with async providers, pass an init function, and return the client:

```ts
import { createOpenFeatureAdapter } from "@flags-sdk/openfeature"

// pass an init function, and return the client
const openFeatureAdapter = createOpenFeatureAdapter(async () => {
  const provider = new YourProviderOfChoice()
  await OpenFeature.setProviderAndWait(provider);
  return OpenFeature.getClient();
});
```

| Option key | Type                                | Description        |
| ---------- | ----------------------------------- | ------------------ |
| `client`   | `Client \| (() => Promise<Client>)` | OpenFeature client |

***

## Identify Users

OpenFeature relies on an [Evaluation Context](https://openfeature.dev/docs/reference/concepts/evaluation-context/) object to evaluate the flags for a given request.

Use the `identify` function to determine the Evaluation Context.

```ts
import type { Identify } from "flags";
import { dedupe, flag } from "flags/next";
import type { EvaluationContext } from "@openfeature/server-sdk";

const identify = dedupe((async ({ headers, cookies }) => {
  // Your own logic to identify the user
  // Identifying the user should rely on reading cookies and headers only, and
  // not make any network requests, as it's important to keep latency low here.
  const user = await getUser(headers, cookies);

  const context: EvaluationContext = {
    targetingKey: user.id,
    // .. other properties ..
  };

  return context;
}));

const openFeatureAdapter = createOpenFeatureAdapter(async () => {
  const provider = new YourProviderOfChoice()
  await OpenFeature.setProviderAndWait(provider);
  return OpenFeature.getClient();
});

export const exampleFlag = flag<boolean, EvaluationContext>({
  key: "example-flag",
  identify,
  defaultValue: false,
  adapter: openFeatureAdapter.booleanValue(),
});
```

<LearnMore icon="arrow" href="/frameworks/next/dedupe">
  Learn more about `dedupe`
</LearnMore>

<LearnMore icon="arrow" href="/principles/evaluation-context">
  Learn more about `identify`
</LearnMore>

***

## Methods

The OpenFeature adapter provides methods for evaluating flags.

### `booleanValue`

```ts
export const exampleFlag = flag<boolean, EvaluationContext>({
  key: 'example-flag',
  identify,
  defaultValue: false,
  adapter: openFeatureAdapter.booleanValue(),
});
```

* `identify` must return the [Evaluation Context](https://openfeature.dev/docs/reference/concepts/evaluation-context/).
* `defaultValue` must be provided when used with the OpenFeature adapter.

### `stringValue`

```ts
export const exampleFlag = flag<string, EvaluationContext>({
  key: 'example-flag',
  identify,
  defaultValue: "",
  adapter: openFeatureAdapter.stringValue(),
});
```

* `identify` must return the [Evaluation Context](https://openfeature.dev/docs/reference/concepts/evaluation-context/).
* `defaultValue` must be provided when used with the OpenFeature adapter.

### `numberValue`

```ts
export const exampleFlag = flag<number, EvaluationContext>({
  key: 'example-flag',
  identify,
  defaultValue: -1,
  adapter: openFeatureAdapter.numberValue(),
});
```

* `identify` must return the [Evaluation Context](https://openfeature.dev/docs/reference/concepts/evaluation-context/).
* `defaultValue` must be provided when used with the OpenFeature adapter.

### `objectValue`

```ts
export const exampleFlag = flag<YourCustomObjectType, EvaluationContext>({
  key: 'example-flag',
  identify,
  defaultValue: {},
  adapter: openFeatureAdapter.objectValue(),
});
```

* `identify` must return the [Evaluation Context](https://openfeature.dev/docs/reference/concepts/evaluation-context/).
* `defaultValue` must be provided when used with the OpenFeature adapter.

### `client`

When the `openFeatureAdapter` was created synchronously, this contains the OpenFeature client.

```ts
openFeatureAdapter.client;
```

When the `openFeatureAdapter` was created asynchronously, this is an async function which returns the OpenFeature client returned by the `init` function.

```ts
await openFeatureAdapter.client();
```

***

## Global Config

The OpenFeature adapter does not integrate with Global Config directly.

We recommend using a native Flags SDK adapter for your provider instead of using the OpenFeature adapter. Native Flags SDK adapters finely tune the SDK for your flag provider, optionally integrate with Global Config and Flags Explorer, and are configured for Edge Runtime compatibility where possible. See [available adapters](/docs/adapters/supported-providers#adapters).

If no native Flags SDK adapter is available, your OpenFeature provider might still allow you to configure Global Config integration.

***

## Flags Explorer

The [Flags Explorer](https://vercel.com/docs/feature-flags/using-vercel-toolbar) is a tool for viewing and overriding flags.

All feature flags using the Flags SDK can be overwritten by the Flags Explorer.

To make the Flags Explorer aware of your OpenFeature flags, you need to provide a route which the Flags Explorer will load your flags metadata from.

```ts title="app/.well-known/vercel/flags/route.ts#next"
import { getProviderData, createFlagsDiscoveryEndpoint } from 'flags/next';
import * as flags from '../../../../flags';

export const GET = createFlagsDiscoveryEndpoint(async (request) => {
  return getProviderData(flags);
});
```

***

## Caveats

### Edge Runtime compatibility

The OpenFeature provider of your choice may not be compatible with Edge Runtime, which can prevent usage in Routing Middleware and Edge Functions. This depends on the provider you use OpenFeature with.

### Flags Explorer metadata

Due to limitations in the OpenFeature specification, the `@flags-sdk/openfeature` adapter is not capable of providing additional metadata like descriptions and available options to the Flags Explorer. Flags Explorer can still display the descriptions and options you define when declaring feature flags in code.

***

## Read More

Read more about OpenFeature, Flags SDK, and the OpenFeature Node.js SDK.

<LearnMore icon="arrow" href="/docs/adapters/supported-providers">
  Learn more about Adapters
</LearnMore>

<LearnMore icon="arrow" href="https://vercel.com/templates/next.js/flags-sdk-openfeature" target="_blank">
  Clone the OpenFeature template
</LearnMore>

<LearnMore icon="arrow" href="https://vercel.com/docs/flags/flags-explorer" target="_blank">
  Learn more about the Flags Explorer
</LearnMore>

<LearnMore icon="arrow" href="https://openfeature.dev/docs/reference/technologies/server/javascript/" target="_blank">
  Learn more about the OpenFeature Node.js SDK
</LearnMore>


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Kameloon
description: Use Kameloon with the Flags SDK
---

# Kameloon



{/*
  DO NOT EDIT THIS FILE DIRECTLY. IT IS AUTO-GENERATED.

  Use pnpm generate-openfeature-providers to generate all OpenFeature providers.
  */}

Kameloon can be used with the Flags SDK through OpenFeature.
Set up the [Flags SDK adapter for OpenFeature](/providers/openfeature),
along with the [Kameloon OpenFeature provider](https://github.com/Kameleoon/openfeature-nodejs) to get started.

## Installation

Install the following packages:

```bash
npm install flags @flags-sdk/openfeature @kameleoon/openfeature-server
```

## Setup

Set up your [Kameloon OpenFeature provider](https://github.com/Kameleoon/openfeature-nodejs) as a Flags SDK adapter.

```ts
// adapter.ts
import { OpenFeature } from "@openfeature/server-sdk";
import { createOpenFeatureAdapter } from "@flags-sdk/openfeature";
import { KameleoonProvider } from "@kameleoon/openfeature-server";

export const openFeatureAdapter = createOpenFeatureAdapter(async () => {
  const provider = new KameleoonProvider({
    siteCode: SITE_CODE,
    credentials: { clientId: CLIENT_ID, clientSecret: CLIENT_SECRET },
  });
  await OpenFeature.setProviderAndWait(provider);
  return OpenFeature.getClient();
});
```

See the [OpenFeature adapter](/providers/openfeature) for more details.

## Usage

Use your newly configured adapter when declaring flags.

```ts
// flags.ts
import { flag } from "flags/next";
import type { EvaluationContext } from "@openfeature/server-sdk";
import { openFeatureAdapter } from "./adapter";

export const exampleFlag = flag<boolean, EvaluationContext>({
  key: "example-flag",
  defaultValue: false,
  adapter: openFeatureAdapter.booleanValue(),
});
```

## Resources

<LearnMore icon="arrow" href="/providers/openfeature">
  Flags SDK adapter for OpenFeature
</LearnMore>

<LearnMore icon="arrow" href="https://github.com/Kameleoon/openfeature-nodejs" target="_blank">
  Kameloon OpenFeature Provider
</LearnMore>

*Note that OpenFeature providers may require additional configuration for optimal performance and compatibility. Setup instructions are provided on a best effort basis. Refer to each provider's own documentation.*

*If there is a native Flags SDK adapter for your provider, we recommend using that instead.
Native Flags SDK adapters tune your flag provider's SDK for optimal performance and integrate with Flags Explorer and Global Config.
See [available adapters](/docs/adapters/supported-providers#adapters).*

*If you are a feature flag provider interested in developing a native adapter please [open a GitHub issue](https://github.com/vercel/flags) to get in touch.*


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: PostHog
description: Use PostHog with the Flags SDK
---

# PostHog



{/*
  DO NOT EDIT THIS FILE DIRECTLY. IT IS AUTO-GENERATED.

  Use pnpm generate-openfeature-providers to generate all OpenFeature providers.
  */}

PostHog can be used with the Flags SDK through OpenFeature.
Set up the [Flags SDK adapter for OpenFeature](/providers/openfeature),
along with the [PostHog OpenFeature provider](https://www.npmjs.com/package/@tapico/node-openfeature-posthog) to get started.

***This provider has a native adapter available at [@flags-sdk/posthog](/providers/posthog), which you should use instead***.

## Installation

Install the following packages:

```bash
npm install flags @flags-sdk/openfeature @tapico/node-openfeature-posthog
```

## Setup

Set up your [PostHog OpenFeature provider](https://www.npmjs.com/package/@tapico/node-openfeature-posthog) as a Flags SDK adapter.

```ts
// adapter.ts
import { OpenFeature } from "@openfeature/server-sdk";
import { createOpenFeatureAdapter } from "@flags-sdk/openfeature";
import { PostHogProvider } from "@tapico/node-openfeature-posthog";

export const openFeatureAdapter = createOpenFeatureAdapter(async () => {
  const provider = new PostHogProvider(/* ... */);
  await OpenFeature.setProviderAndWait(provider);
  return OpenFeature.getClient();
});
```

See the [OpenFeature adapter](/providers/openfeature) for more details.

## Usage

Use your newly configured adapter when declaring flags.

```ts
// flags.ts
import { flag } from "flags/next";
import type { EvaluationContext } from "@openfeature/server-sdk";
import { openFeatureAdapter } from "./adapter";

export const exampleFlag = flag<boolean, EvaluationContext>({
  key: "example-flag",
  defaultValue: false,
  adapter: openFeatureAdapter.booleanValue(),
});
```

## Resources

<LearnMore icon="arrow" href="/providers/openfeature">
  Flags SDK adapter for OpenFeature
</LearnMore>

<LearnMore icon="arrow" href="https://www.npmjs.com/package/@tapico/node-openfeature-posthog" target="_blank">
  PostHog OpenFeature Provider
</LearnMore>

*Note that OpenFeature providers may require additional configuration for optimal performance and compatibility. Setup instructions are provided on a best effort basis. Refer to each provider's own documentation.*

*If there is a native Flags SDK adapter for your provider, we recommend using that instead.
Native Flags SDK adapters tune your flag provider's SDK for optimal performance and integrate with Flags Explorer and Global Config.
See [available adapters](/docs/adapters/supported-providers#adapters).*

*If you are a feature flag provider interested in developing a native adapter please [open a GitHub issue](https://github.com/vercel/flags) to get in touch.*


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Split
description: Use Split with the Flags SDK
---

# Split



{/*
  DO NOT EDIT THIS FILE DIRECTLY. IT IS AUTO-GENERATED.

  Use pnpm generate-openfeature-providers to generate all OpenFeature providers.
  */}

Split can be used with the Flags SDK through OpenFeature.
Set up the [Flags SDK adapter for OpenFeature](/providers/openfeature),
along with the [Split OpenFeature provider](https://github.com/splitio/split-openfeature-provider-js) to get started.

## Installation

Install the following packages:

```bash
npm install flags @flags-sdk/openfeature @splitsoftware/openfeature-js-split-provider @openfeature/js-sdk @splitsoftware/splitio
```

## Setup

Set up your [Split OpenFeature provider](https://github.com/splitio/split-openfeature-provider-js) as a Flags SDK adapter.

```ts
// adapter.ts
import { OpenFeature } from "@openfeature/server-sdk";
import { createOpenFeatureAdapter } from "@flags-sdk/openfeature";
import { OpenFeature } from "@openfeature/js-sdk";
import { SplitFactory } from "@splitsoftware/splitio";
import { OpenFeatureSplitProvider } from "@splitsoftware/openfeature-js-split-provider";

export const openFeatureAdapter = createOpenFeatureAdapter(async () => {
  const authorizationKey = 'your auth key';
  const splitClient = SplitFactory({core: { authorizationKey }}).client();
  const provider = new OpenFeatureSplitProvider({ splitClient });

  await OpenFeature.setProviderAndWait(provider);
  return OpenFeature.getClient();
});
```

See the [OpenFeature adapter](/providers/openfeature) for more details.

## Usage

Use your newly configured adapter when declaring flags.

```ts
// flags.ts
import { flag } from "flags/next";
import type { EvaluationContext } from "@openfeature/server-sdk";
import { openFeatureAdapter } from "./adapter";

export const exampleFlag = flag<boolean, EvaluationContext>({
  key: "example-flag",
  defaultValue: false,
  adapter: openFeatureAdapter.booleanValue(),
});
```

## Resources

<LearnMore icon="arrow" href="/providers/openfeature">
  Flags SDK adapter for OpenFeature
</LearnMore>

<LearnMore icon="arrow" href="https://github.com/splitio/split-openfeature-provider-js" target="_blank">
  Split OpenFeature Provider
</LearnMore>

*Note that OpenFeature providers may require additional configuration for optimal performance and compatibility. Setup instructions are provided on a best effort basis. Refer to each provider's own documentation.*

*If there is a native Flags SDK adapter for your provider, we recommend using that instead.
Native Flags SDK adapters tune your flag provider's SDK for optimal performance and integrate with Flags Explorer and Global Config.
See [available adapters](/docs/adapters/supported-providers#adapters).*

*If you are a feature flag provider interested in developing a native adapter please [open a GitHub issue](https://github.com/vercel/flags) to get in touch.*


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Tggl
description: Use Tggl with the Flags SDK
---

# Tggl



{/*
  DO NOT EDIT THIS FILE DIRECTLY. IT IS AUTO-GENERATED.

  Use pnpm generate-openfeature-providers to generate all OpenFeature providers.
  */}

Tggl can be used with the Flags SDK through OpenFeature.
Set up the [Flags SDK adapter for OpenFeature](/providers/openfeature),
along with the [Tggl OpenFeature provider](https://tggl.io/developers/sdks/open-feature/node) to get started.

## Installation

Install the following packages:

```bash
npm install flags @flags-sdk/openfeature openfeature-server-tggl-provider
```

## Setup

Set up your [Tggl OpenFeature provider](https://tggl.io/developers/sdks/open-feature/node) as a Flags SDK adapter.

```ts
// adapter.ts
import { OpenFeature } from "@openfeature/server-sdk";
import { createOpenFeatureAdapter } from "@flags-sdk/openfeature";
import { TgglServerProvider } from 'openfeature-server-tggl-provider';


export const openFeatureAdapter = createOpenFeatureAdapter(async () => {
  const provider = new TgglServerProvider('API_KEY');
  await OpenFeature.setProviderAndWait(provider);
  return OpenFeature.getClient();
});
```

See the [OpenFeature adapter](/providers/openfeature) for more details.

## Usage

Use your newly configured adapter when declaring flags.

```ts
// flags.ts
import { flag } from "flags/next";
import type { EvaluationContext } from "@openfeature/server-sdk";
import { openFeatureAdapter } from "./adapter";

export const exampleFlag = flag<boolean, EvaluationContext>({
  key: "example-flag",
  defaultValue: false,
  adapter: openFeatureAdapter.booleanValue(),
});
```

## Resources

<LearnMore icon="arrow" href="/providers/openfeature">
  Flags SDK adapter for OpenFeature
</LearnMore>

<LearnMore icon="arrow" href="https://tggl.io/developers/sdks/open-feature/node" target="_blank">
  Tggl OpenFeature Provider
</LearnMore>

*Note that OpenFeature providers may require additional configuration for optimal performance and compatibility. Setup instructions are provided on a best effort basis. Refer to each provider's own documentation.*

*If there is a native Flags SDK adapter for your provider, we recommend using that instead.
Native Flags SDK adapters tune your flag provider's SDK for optimal performance and integrate with Flags Explorer and Global Config.
See [available adapters](/docs/adapters/supported-providers#adapters).*

*If you are a feature flag provider interested in developing a native adapter please [open a GitHub issue](https://github.com/vercel/flags) to get in touch.*


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Dashboard Pages
description: Use feature flags on dynamic pages.
---

# Dashboard Pages



Dashboard pages are rendered at request time, and may require authenticated
users.

The example below shows how to use feature flags to show a feature to
specific users on a dashboard page. They are flagged in based on a specific cookie. The buttons below allow you to either act as a flagged in user
or as a regular user.

<IframeBrowser src="sveltekit-snippets:/examples/dashboard-pages" codeSrc="https://github.com/vercel/flags/tree/main/examples/sveltekit-example/src/routes/examples/dashboard-pages" />

## Definition

The example above works by first defining a feature flag.

```ts title="src/lib/flags.ts"
import { flag } from 'flags/sveltekit';

export const showNewDashboard = flag<boolean>({
  key: 'showNewDashboard',
  decide({ cookies }) {
    return cookies.get('showNewDashboard')?.value === 'true';
  },
});
```

The example reads the value directly from the cookie. In a real
dashboard you would likely read a signed JWT instead.

## Usage

Any server `load` functions can evaluate the feature flag by calling it.

```ts title="src/routes/+page.server.ts"
import type { PageServerLoad } from './$types';
import { showNewDashboard } from '$lib/flags';

export const load: PageServerLoad = async () => {
  const dashboard = await showNewDashboard();

  return {
    title: dashboard ? 'New Dashboard' : `Old Dashboard`,
  };
};
```

Since dashboard pages are typically dynamic anyway, the async call to
evaluate the feature flag fits right in.

## Evaluation Context

Feature Flags used on dashboards will usually run in the Serverless
Function Region, close to the database. This means it is acceptable for
a feature flag's `decide` function to read the database
when establishing the evaluation context. However, ideally, it would
only read from the JWT as this will lead to lower overall latency.


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Marketing Pages
description: Use feature flags on static pages.
---

# Marketing Pages



import Link from 'next/link';

This example shows how to use feature flags for marketing pages.
Marketing pages are typically static, and served from a CDN at the edge.

When A/B testing on marketing pages it's important to avoid layout
shift and jank, and to keep the pages static. At first glance this seems
at odds with the dynamic nature of feature flags. This example shows
how to keep a page static and serveable from the CDN even when running
multiple A/B tests on the page.

## Precomputing

The approach used to keep pages static even when using feature flags on them is described in more detail on the Precompute section.
At a high level we use Routing Middleware and SvelteKit's `reroute` hook to rewrite the incoming request between static versions of the page.
These static versions represent the different feature flag states and can be computed at build time or at request time.
If they are computed at request time we can use Incremental Static Regeneration (ISR).
Relying on ISR avoids the combinatory explosion which would otherwise increase the build time, while simultaneously allowing pages to stay cached after the first time they were requested.

<LearnMore href="/frameworks/sveltekit/precompute" icon="arrow">
  Learn more about `precompute`
</LearnMore>

## Identifying

This example uses a random id stored in a cookie to target users.
SvelteKit can't set cookies while rendering pages, because they would become part of the ISR response which we don't want (every user would get the same visitor cookie).
You therefore must create a cookie (for example with a random id) as part of Routing Middleware and `reroute`. Here's how you do it within middleware (similarly for reroute):

```ts title="middleware.ts"
import { randomUUID } from 'crypto';
import { rewrite } from '@vercel/edge';
import { parse } from 'cookie';
import { normalizeUrl } from '@sveltejs/kit';
import { precompute } from 'flags/sveltekit';
import {
  marketingFlags,
  computeInternalRoute,
  createVisitorId,
} from './src/lib/precomputed-flags';
import { examplePrecomputed } from './flags';

export const config = {
  matcher: ['/examples/marketing-pages'],
};

export default async function middleware(request: Request) {
  const { url, denormalize } = normalizeUrl(request.url);

  // Retrieve cookies which contain the feature flags.
  let visitorId = parse(request.headers.get('cookie') ?? '').visitorId || '';

  if (!visitorId) {
    visitorId = createVisitorId();
    request.headers.set('x-visitorId', visitorId); // cookie is not available on the initial request
  }

  return rewrite(
    // Get destination URL based on the feature flag
    denormalize(await computeInternalRoute(url.pathname, request)),
  );
}

// Similar logic for `reroute`
```

The `identify` function which is used by flags then reads that id to generate the entity, which the flags then use to decide which variant to show.
By making `identify` a shared function its call can be deduplicated between flags, i.e. `identify` is only called once per request.

```ts title="src/lib/flags.ts"
import { flag } from 'flags/sveltekit';

interface Entities {
  visitorId?: string;
}

function identify({
  cookies,
  headers,
}: {
  cookies: ReadonlyRequestCookies;
  headers: ReadonlyHeaders;
}): Entities {
  const visitorId =
    cookies.get('visitorId')?.value ?? headers.get('x-visitorId');

  if (!visitorId) {
    throw new Error(
      'Visitor ID not found - should have been set by middleware or within api/reroute',
    );
  }

  return { visitorId };
}

export const firstMarketingABTest = flag<boolean, Entities>({
  key: 'firstMarketingABTest',
  description: 'Example of a precomputed flag',
  identify,
  decide({ entities }) {
    if (!entities?.visitorId) return false;

    // Use any kind of deterministic method that runs on the visitorId
    return /^[a-n0-5]/i.test(entities?.visitorId);
  },
});

// ...
```

<LearnMore href="/frameworks/sveltekit/evaluation-context" icon="arrow">
  Learn more about `identify`
</LearnMore>

## Ensuring the generated id is always available

When a user visits the page for the first time they will not have the{" "}
`visitorId` cookie. Routing Middleware (or `reroute` indirectly via the API call) will generate
an id and store that in a cookie. However, this means the page will not
see the generated cookie as it will only be supplied with the next
request. This means a dynamic page would have no knowledge of the
generated id.

To solve this issue the approach above also sets a{" "}
`x-visitorId` request header from Routing Middleware and the API,
which holds the parsed or generated id. This allows the{" "}
`identify` function to always see the id by
either reading it from the cookie or the header.

## Example

The example below shows the usage of two feature flags on a static page.
These flags represent two A/B tests which you could be running simultaneously.

<IframeBrowser src="sveltekit-snippets:/examples/marketing-pages" codeSrc="https://github.com/vercel/flags/tree/main/examples/sveltekit-example/src/routes/examples/marketing-pages" />


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Dashboard Pages
description: Use feature flags on dashboard pages.
---

# Dashboard Pages



Dashboard pages are rendered at request time, and may require authenticated
users.

The example below shows how to use feature flags to show a feature to
specific users on a dashboard page. They are flagged in based on their
user id. The buttons below allow you to either act as a flagged in user
or as a regular user.

<IframeBrowser src="snippets:/examples/dashboard-pages" codeSrc="https://github.com/vercel/flags/tree/main/examples/snippets/app/examples/dashboard-pages" />

## Definition

The example above works by first defining a feature flag.

```tsx title="flags.tsx#next"
import type { ReadonlyRequestCookies } from 'flags';
import { flag, dedupe } from 'flags/next';

interface Entities {
  user?: { id: string };
}

const identify = dedupe(
  ({ cookies }: { cookies: ReadonlyRequestCookies }): Entities => {
    const userId = cookies.get('dashboard-user-id')?.value;
    return { user: userId ? { id: userId } : undefined };
  },
);

export const dashboardFlag = flag<boolean, Entities>({
  key: 'dashboard-flag',
  identify,
  decide({ entities }) {
    if (!entities?.user) return false;
    // Allowed users could be loaded from Global Config or elsewhere
    const allowedUsers = ['user1'];

    return allowedUsers.includes(entities.user.id);
  },
});
```

The definition includes an `identify` function. The
`identify` function is used to establish the evaluation
context.

The example reads the user id directly from the cookie. In a real
dashboard you would likely read a signed JWT instead.

## Usage

Any server-side code can evaluate the feature flag by calling it.

```tsx title="app/page.tsx"
export default async function DashboardPage() {
  const dashboard = await dashboardFlag();
  // do something with the flag
  return <div>Dashboard</div>;
}
```

Since dashboard pages are typically dynamic anyhow the async call to
evaluate the feature flag should fit right in.

## Identifying

The example flag calls `identify` to establish the evaluation
context. This function returns the entities that are used to evaluate
the feature flag.

The `decide` function then later gets access to the{" "}
`entities` returned from the `identify` function.

<LearnMore href="/principles/evaluation-context" icon="arrow">
  Learn more about `identify`
</LearnMore>

## Evaluation context

Feature flags used on dashboards will usually run in the Serverless
Function Region, close to the database. This means it is acceptable for
a feature flag's `decide` function to read the database
when establishing the evaluation context. However, ideally, it would
only read from the JWT as this will lead to lower overall latency.

## Deduplication

The `identify` call uses `dedupe` to avoid
duplicate work when multiple feature flags depend on the same evaluation
context.

<LearnMore href="/frameworks/next/dedupe" icon="arrow">
  Learn more about `dedupe`
</LearnMore>


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Marketing Pages
description: Use feature flags on marketing pages.
---

# Marketing Pages



import Link from 'next/link';

This example shows how to use feature flags for marketing pages.
Marketing pages are typically static, and served from a CDN at the edge.

When A/B testing on marketing pages it's important to avoid layout
shift and jank, and to keep the pages static. At first glance this seems
at odds with the dynamic nature of feature flags. This example shows
how to keep a page static and serveable from the CDN even when running
multiple A/B tests on the page.

## Precomputing

The approach used to keep pages static even when using feature flags on
them is described in more detail on the [Precompute](/principles/precompute) section. At a high level we use Proxy to rewrite the incoming request between static versions of
the page. These static versions represent the different feature flag states and
can be computed at build time or at request time. If they are computed at
request time we can use Incremental Static Regeneration (ISR). Relying on ISR
avoids the combinatory explosion which would otherwise increase the build time,
while simultaneously allowing pages to stay cached after the first time they
were requested.

<LearnMore href="/principles/precompute" icon="arrow">
  Learn more about `precompute`
</LearnMore>

## Identifying

This example uses a random id stored in a cookie to target users.
Next.js can't set cookies while rendering pages, so you must use
Proxy to generate the random id and store it in a cookie.

This also allows the page to stay static as the page will never access
cookies. Instead, the flags using cookies will be evaluated in Routing
Functions and only the resulting flags will be available when rendering
the page.

```tsx title="proxy.ts#next"
import { precompute } from 'flags/next';
import { type NextRequest, NextResponse } from 'next/server';
import { marketingFlags } from './flags';
import { getOrGenerateVisitorId } from './get-or-generate-visitor-id';

export async function marketingMiddleware(request: NextRequest) {
  // assign a cookie to the visitor
  const visitorId = await getOrGenerateVisitorId(
    request.cookies,
    request.headers,
  );

  // precompute the flags
  const code = await precompute(marketingFlags);

  // rewrite the page with the code and set the cookie
  return NextResponse.rewrite(
    new URL(`/examples/marketing-pages/${code}`, request.url),
    {
      headers: {
        // Set the cookie on the response
        'Set-Cookie': `marketing-visitor-id=${visitorId}; Path=/`,
        // Add a request header, so the page knows the generated id even
        // on the first-ever request which has no request cookie yet.
        //
        // This is later used by the getOrGenerateVisitorId function.
        'x-marketing-visitor-id': visitorId,
      },
    },
  );
}
```

The `getOrGenerateVisitorId` function generates a random id,
or returns the one stored in a cookie if one already exists. The
function is further [deduplicated](/docs/concepts/dedupe) to
ensure it generates the same id for the same request, even when called
multiple times.

```ts title="get-or-generate-visitor-id.ts#next"
import { nanoid } from 'nanoid';
import { dedupe } from 'flags/next';
import type { ReadonlyHeaders, ReadonlyRequestCookies } from 'flags';
import type { NextRequest } from 'next/server';

const generateId = dedupe(async () => nanoid());

// This function is not deduplicated, as it is called with
// two different cookies objects, so it can not be deduplicated.
//
// However, the generateId function will always generate the same id for the
// same request, so it is safe to call it multiple times within the same runtime.
export const getOrGenerateVisitorId = async (
  cookies: ReadonlyRequestCookies | NextRequest['cookies'],
  headers: ReadonlyHeaders | NextRequest['headers'],
) => {
  // check cookies first
  const cookieVisitorId = cookies.get('marketing-visitor-id')?.value;
  if (cookieVisitorId) return cookieVisitorId;

  // check headers in case middleware set a cookie on the response, as it will
  // not be present on the initial request
  const headerVisitorId = headers.get('x-marketing-visitor-id');
  if (headerVisitorId) return headerVisitorId;

  // if no visitor id is found, generate a new one
  return generateId();
};
```

Having a reliable `getOrGenerateVisitorId` function means we
can call it in Edge Runtime and in our flag's `identify`{" "}
function and both will see the exact same id, even when no cookie was
present initially.

```tsx title="flags.tsx#next"
// identify who is requesting the page
const identify = dedupe(
  async ({
    cookies,
  }: {
    cookies: ReadonlyRequestCookies;
  }): Promise<Entities> => {
    const visitorId = await getOrGenerateVisitorId(cookies);
    return { visitor: visitorId ? { id: visitorId } : undefined };
  },
);

export const marketingAbTest = flag<boolean, Entities>({
  key: 'marketing-ab-test-flag',
  // use identify to establish the evaluation context,
  // which will be passed as "entities" to the decide function
  identify,
  decide({ entities }) {
    if (!entities?.visitor) return false;
    return /^[a-n0-5]/i.test(entities.visitor.id);
  },
});
```

<LearnMore href="/principles/evaluation-context" icon="arrow">
  Learn more about `identify`
</LearnMore>

## Ensuring the generated id is always available

When a user visits the page for the first time they will not have the{" "}
`marketing-visitor-id` cookie. Proxy will generate
an id and store that in a cookie. However, this means the page will not
see the generated cookie as it will only be supplied with the next
request. This means a dynamic page would have no knowledge of the
generated id.

To solve this issue the approach above also sets a{" "}
`x-marketing-visitor-id` request header from Proxy,
which holds the parsed or generated id. This allows the{" "}
`getOrGenerateVisitorId` function to always see the id by
either reading it from the cookie or the header. This works even when
the id was freshly generated by Proxy and no cookie is present
on the request.

This is only relevant when the underlying page is dynamic, as a static
page can by definition not read any cookies or headers.

## Example

The example below shows the usage of two feature flags on a static page.
These flags represent two A/B tests which you could be running
simultaneously.

<IframeBrowser src="snippets:/examples/marketing-pages" codeSrc="https://github.com/vercel/flags/tree/main/examples/snippets/app/examples/marketing-pages" />


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Proxy
description: How to use feature flags in Proxy to serve different static variants of a page.
---

# Proxy



import Link from 'next/link';

This example works by using a feature flag in Proxy to then
rewrite the request to a different page. Rewriting the request means the
user-facing URL shown in the browser stays the same, while different
content is served for different visitors. As the underlying{" "}

`variant-on` and `variant-off` pages are static, the CDN
can serve these at the edge.

```tsx title="proxy.ts#next"
import { type NextRequest, NextResponse } from 'next/server';
import { basicEdgeMiddlewareFlag } from './flags';

export const config = {
  matcher: ['/examples/feature-flags-in-proxy'],
};

export async function proxy(request: NextRequest) {
  const active = await basicEdgeMiddlewareFlag();
  const variant = active ? 'variant-on' : 'variant-off';

  return NextResponse.rewrite(
    new URL(
      `/examples/feature-flags-in-proxy/${variant}`,
      request.url,
    ),
  );
}
```

<IframeBrowser src="snippets:/examples/feature-flags-in-proxy" codeSrc="https://github.com/vercel/flags/tree/main/examples/snippets/app/examples/feature-flags-in-proxy" />

## Advanced examples

Using feature flags in Proxy as shown in this example covers the
basics. This approach does not scale well when you are using
multiple feature flags on the same page or when you are using the same
feature flag on multiple pages. We recommend using [precompute](/principles/precompute) for more advanced use cases, which solves these challenges.


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)

---
title: Suspense Fallbacks
description: How to use feature flags with Suspense fallbacks to serve variants of a Partial Prerendered shell.
---

# Suspense Fallbacks



import Link from 'next/link';

This example shows how to create and switch between multiple variants of a
Partial Prerendering shell by combining Suspense fallbacks with
precomputed feature flags.

## Precompute

Precomputing feature flags and partial prerendering pages can be
combined with Suspense fallbacks to predict the appropriate fallback of
a Suspense boundary and thus show a skeleton which will match the
predicted state.

```tsx title="app/page.tsx"
function Example() {
  const hasAuth = await hasAuthCookieFlag();

  return (
    <Suspense fallback={hasAuth ? <AuthedSkeleton /> : <UnauthedSkeleton />}>
      <Dashboard />
    </Suspense>
  );
}
```

<LearnMore href="/principles/precompute" icon="arrow">
  Learn more about precomputing
</LearnMore>

In this example the `hasAuthCookieFlag` flag checks whether
the user has an authentication cookie without actually authenticating
the user yet. Authenticating the user and fetching their profile
information is left to the `Dashboard` component.

Checking the existence of an authentication cookie allows the application to
know whether the user is likely to be signed in or definitely signed
out. It can then use this information to either show the *Sign in*{" "}
button or render the skeleton of the authenticated state while that
resolves.

## Partial Prerendering

This technique requires using [Partial Prerendering](https://nextjs.org/learn/dashboard-app/partial-prerendering) to make the fallback part of the prerendered shell, which is then served
statically. Essentially two different shells get created for this page:
One shell with a skeleton for the authenticated state, and another shell
for the unauthenticated page.

## Use case

Marketing pages frequently need to either show a *Dashboard* button
or *Sign in* and *Sign up* buttons depending on whether the
user is authenticated. The approach outlined here allows serving the
marketing pages statically without blocking the response on the auth
state. Partial Prerendering then streams the auth state once it is
resolved as part of the same response.

## Example

<IframeBrowser src="snippets:/examples/suspense-fallbacks" codeSrc="https://github.com/vercel/flags/tree/main/examples/snippets/app/examples/suspense-fallbacks" />

Reload the frame while signed out to see the *Sign in* button
immediately, without any layout shift. Then click *Sign in* and
reload the page again to see the skeleton for the authenticated state,
again without any layout shift.

## Bypassing Proxy altogether

The approach outlined above is already performant. You can further
improve latency by avoiding the need to invoke Routing
Functions.

Using the `rewrites` in `next.config.js` allows
detecting whether an authentication cookie is present and rewriting the
request appropriately. An example of this technique is shown [in this pull request](https://github.com/RhysSullivan/galaxyscale/pull/2).

This approach is only an alternative for the authentication scenario as
it does not scale across multiple feature flags, and is limited to
situations the Next.js rewrites configuration can express. The
additional complexity is typically not worth the single digit
milliseconds it saves.


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)