llms.txt
# Flags SDK documentation
> Flags SDK is a free, open-source library for using feature flags in Next.js and SvelteKit.
## When to use Flags SDK
- Category: Feature Flags
- Audience: Application developers, Framework teams
Common use cases:
- Implement feature flags as code
- Connect feature flags to providers
- Precompute feature flags in supported frameworks
## Documentation
---
title: Data Locality
url: "https://flags-sdk.dev/docs/principles/data-locality"
docs_index: /llms.txt
lastUpdated: 2026-10-09
---
> For an index of all documentation, see [/llms.txt](/llms.txt).
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.
[Learn more about `dedupe`](/frameworks/next/dedupe)
## 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.
url: "https://flags-sdk.dev/docs/principles/evaluation-context"
docs_index: /llms.txt
lastUpdated: 2026-10-09
---
> For an index of all documentation, see [/llms.txt](/llms.txt).
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';
},
});
```
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.
[Learn more about `dedupe`](/frameworks/next/dedupe)
## 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.
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';
},
});
```
[See the Marketing Pages example](/frameworks/next/guides/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: Flags as Code
url: "https://flags-sdk.dev/docs/principles/flags-as-code"
docs_index: /llms.txt
lastUpdated: 2026-10-09
---
> For an index of all documentation, see [/llms.txt](/llms.txt).
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';
},
});
```
[Learn more about `identify`](/principles/evaluation-context)
## 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),
});
```
[Learn more about `adapters`](/providers)
---
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.
url: "https://flags-sdk.dev/docs/principles/precompute"
docs_index: /llms.txt
lastUpdated: 2026-10-09
---
> For an index of all documentation, see [/llms.txt](/llms.txt).
<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.
[<div>
Learn how to implement the precompute pattern in Next.js
</div>](/frameworks/next/precompute)
[<div>
Learn how to implement the precompute pattern in SvelteKit
</div>](/frameworks/sveltekit/precompute)
---
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
url: "https://flags-sdk.dev/docs/principles/server-side-vs-client-side"
docs_index: /llms.txt
lastUpdated: 2026-10-09
---
> For an index of all documentation, see [/llms.txt](/llms.txt).
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).
[See the Marketing Pages example](/frameworks/next/guides/marketing-pages)
## 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: Custom Adapters
description: Integrate any feature flag provider with the Flags SDK using a custom adapter.
url: "https://flags-sdk.dev/docs/providers/custom-adapters"
docs_index: /llms.txt
lastUpdated: 2026-10-09
---
> For an index of all documentation, see [/llms.txt](/llms.txt).
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.
**Prompt for coding agents**
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.
Adapters conceptually replace the `decide` and `origin` parts of a flag de