Documentation

Everything you need to integrate feature flags with Flaglayer.

Quick Start

Pick your framework and get feature flags running in under two minutes.

React

1. Install

npm install @flaglayer/react

2. Wrap your app

import { FlagProvider } from '@flaglayer/react';
function App() {
return (
<FlagProvider
apiKey="fl_dev_..."
context={{ userId: 'user-123' }}
>
<MyComponent />
</FlagProvider>
);
}

3. Use a flag

import { useBooleanFlag } from '@flaglayer/react';
function MyComponent() {
const { value: showDashboard, loading } = useBooleanFlag('new-dashboard', false);
if (loading) return <Spinner />;
return showDashboard ? <NewDashboard /> : <OldDashboard />;
}

Next.js

1. Install

npm install @flaglayer/nextjs

2. Client components

Same API as @flaglayer/react — import from @flaglayer/nextjs instead.

import { FlagProvider, useBooleanFlag } from '@flaglayer/nextjs';

3. Server components

import { createFlagLayerServer } from '@flaglayer/nextjs/server';
const fl = createFlagLayerServer({
apiKey: process.env.FLAGLAYER_API_KEY!,
});
export default async function Page() {
const result = await fl.evaluate('new-dashboard', { userId: 'user-123' });
return result.value ? <NewDashboard /> : <OldDashboard />;
}

Node.js

1. Install

npm install @flaglayer/node

2. Evaluate flags

import { FlagLayer } from '@flaglayer/node';
const fl = new FlagLayer({
apiKey: process.env.FLAGLAYER_API_KEY!,
});
const result = await fl.evaluate('new-dashboard', { userId: 'user-123' });
console.log(result.value); // true
console.log(result.reason); // 'TARGETING_MATCH'

Concepts

Environments

Each project has separate environments (e.g. development, staging, production). Flags can have different rules per environment, and each environment has its own API keys. This lets you test flag changes in staging before rolling them out to production.

Flags & Rules

A flag is a named toggle that returns a typed value (boolean, string, or number). Each flag can have targeting rules per environment that control who sees which value. Rules include an enabled/disabled toggle, a rollout percentage, and optional user targeting.

Targeting

Targeting lets you control exactly who sees a flag. You can target by:

  • Rollout percentage — gradually roll out to a percentage of users using consistent hashing (same user always gets the same result)
  • User allowlist — enable for specific user IDs
  • Email allowlist — enable for specific emails, with wildcard support (e.g. *@company.com)

API Keys

API keys are scoped to an environment. Use them in your SDK client or API requests to authenticate. Keys are hashed on the server — you can only see the full key once when you create it. You can revoke and create new keys at any time.

SDK Reference

@flaglayer/sdk

The core JavaScript/TypeScript client. Works in any JS environment. Maintains a local cache of flags after calling identify().

npm install @flaglayer/sdk

Constructor

import { FlagLayer } from '@flaglayer/sdk';
const fl = new FlagLayer({
apiKey: 'fl_dev_...', // Required
baseUrl: 'https://...', // Optional, defaults to https://api.flaglayer.com
prefix: 'exp_', // Optional, only fetch flags matching this prefix
});

Methods

identify(context) — Set the user context and fetch all flags into the local cache.

await fl.identify({ userId: 'user-123', email: 'user@example.com' });

evaluate(flagKey, context) — Evaluate a single flag via API call. Returns a FlagResult.

const result = await fl.evaluate('new-feature', { userId: 'user-123' });
// result = { value: true, reason: 'TARGETING_MATCH' }

getValue(flagKey) — Get the raw value of a cached flag.

const val = fl.getValue('theme'); // boolean | string | number | undefined

getBooleanValue(flagKey, defaultValue?) — Get a boolean flag from cache with optional default.

const enabled = fl.getBooleanValue('new-feature', false);

getStringValue(flagKey, defaultValue?) / getNumberValue(flagKey, defaultValue?) — Typed getters for string and number flags.

const plan = fl.getStringValue('plan-tier', 'free');
const limit = fl.getNumberValue('rate-limit', 100);

getFlags() — Get all cached flags as a Record<string, FlagResult>.

const flags = fl.getFlags();
// { 'new-feature': { value: true, reason: 'TARGETING_MATCH' }, ... }

refresh() — Re-fetch flags with the current context.

await fl.refresh();

@flaglayer/react

React hooks and provider for feature flags. Handles flag fetching, caching, and re-rendering automatically.

npm install @flaglayer/react

FlagProvider

Wrap your app with the provider. It initializes the SDK client and provides flag state to all child hooks.

import { FlagProvider } from '@flaglayer/react';
function App() {
return (
<FlagProvider
apiKey="fl_dev_..." // Required
context={{ userId: 'user-123' }} // Required — EvaluationContext
baseUrl="https://..." // Optional, custom API URL
prefix="exp_" // Optional, filter flags by prefix
fallback={{ 'feature': { value: false, reason: 'DEFAULT' } }} // Optional
refetchOnWindowFocus={true} // Optional, default: false
refetchInterval={30000} // Optional, ms — 0 to disable
>
<MyComponent />
</FlagProvider>
);
}

Hooks

useBooleanFlag(flagKey, defaultValue?) — Boolean flag with loading state. Recommended for on/off toggles.

import { useBooleanFlag } from '@flaglayer/react';
const { value, loading } = useBooleanFlag('dark-mode', false);

useStringFlag(flagKey, defaultValue?) — String flag with loading state. Useful for A/B test variants, themes, etc.

import { useStringFlag } from '@flaglayer/react';
const { value: variant, loading } = useStringFlag('checkout-variant', 'control');

useNumberFlag(flagKey, defaultValue?) — Number flag with loading state. Good for rate limits, thresholds, etc.

import { useNumberFlag } from '@flaglayer/react';
const { value: maxItems, loading } = useNumberFlag('max-cart-items', 10);

useFlagValue(flagKey) — Any typed flag value. Returns boolean | string | number | undefined.

import { useFlagValue } from '@flaglayer/react';
const { value, loading } = useFlagValue('my-flag');

useFlags() — All flags at once.

import { useFlags } from '@flaglayer/react';
const { flags, loading } = useFlags();
// flags: Record<string, FlagResult>

useFlagLayer() — Access the underlying FlagLayer client for manual operations.

import { useFlagLayer } from '@flaglayer/react';
const client = useFlagLayer();
await client.refresh();

useFlaglayerContext() — Full context with flags, loading, error, client, and refetch function.

import { useFlaglayerContext } from '@flaglayer/react';
const { flags, loading, error, client, refetch } = useFlaglayerContext();

@flaglayer/nextjs

Next.js integration with server-side evaluation, client provider, and routing middleware.

npm install @flaglayer/nextjs

Client Components

Re-exports FlagProvider, useBooleanFlag, useStringFlag, useNumberFlag, useFlagValue, useFlags, and useFlagLayer from @flaglayer/react. Same API — just import from @flaglayer/nextjs.

Server Components

Stateless server-side evaluation. Each call hits the API directly — no local cache.

import { createFlagLayerServer } from '@flaglayer/nextjs/server';
const fl = createFlagLayerServer({
apiKey: process.env.FLAGLAYER_API_KEY!,
});
// Evaluate a single flag
const result = await fl.evaluate('new-feature', { userId: 'user-123' });
// result = { value: true, reason: 'TARGETING_MATCH' }
// Evaluate all flags
const flags = await fl.evaluateAll({ userId: 'user-123' });
// With prefix filter
const expFlags = await fl.evaluateAll(
{ userId: 'user-123' },
{ prefix: 'exp_' }
);

Server Value Extractors

Helper methods to extract typed values from FlagResult objects.

const result = await fl.evaluate('plan-tier', { userId: 'user-123' });
const enabled = fl.getBooleanValue(result, false);
const plan = fl.getStringValue(result, 'free');
const limit = fl.getNumberValue(result, 100);

Routing Middleware

Flag-based routing at the edge. Rewrite or redirect based on flag values — no client-side flicker.

// middleware.ts
import { createFlagLayerMiddleware } from '@flaglayer/nextjs/middleware';
export default createFlagLayerMiddleware({
apiKey: process.env.FLAGLAYER_API_KEY!,
rules: [
{ flag: 'new-pricing', match: '/pricing', rewrite: '/pricing-v2' },
{ flag: 'maintenance', match: '/app/*', redirect: '/maintenance' },
],
getContext: (req) => ({
userId: req.cookies.get('uid')?.value ?? 'anon',
}),
});

@flaglayer/node

Server-side Node.js client. Stateless — each call hits the API. Ideal for backend services, Express handlers, and serverless functions.

npm install @flaglayer/node
import { FlagLayer } from '@flaglayer/node';
const fl = new FlagLayer({
apiKey: process.env.FLAGLAYER_API_KEY!,
baseUrl: 'https://...', // Optional, defaults to https://api.flaglayer.com
});
// Evaluate a single flag — returns FlagResult
const result = await fl.evaluate('new-feature', { userId: 'user-123' });
console.log(result.value); // true
console.log(result.reason); // 'TARGETING_MATCH'
// Evaluate all flags (with optional prefix filter)
const flags = await fl.evaluateAll(
{ userId: 'user-123' },
{ prefix: 'exp_' }
);
// Extract typed values from results
const enabled = fl.getBooleanValue(result, false);
const plan = fl.getStringValue(result, 'free');
const limit = fl.getNumberValue(result, 100);

Testing

The React provider supports mock props for deterministic testing — no API calls needed.

import { FlagProvider } from '@flaglayer/react';
// Mock specific flag values
<FlagProvider
apiKey="test"
context={{ userId: 'test-user' }}
mockFlags={{
'new-dashboard': { value: true, reason: 'TARGETING_MATCH' },
'dark-mode': { value: false, reason: 'DEFAULT' },
}}
>
<ComponentUnderTest />
</FlagProvider>
// Simulate loading state
<FlagProvider
apiKey="test"
context={{ userId: 'test-user' }}
mockLoading={true}
>
<ComponentUnderTest />
</FlagProvider>
// Simulate error state
<FlagProvider
apiKey="test"
context={{ userId: 'test-user' }}
mockError={new Error('Network error')}
>
<ComponentUnderTest />
</FlagProvider>

Types

Key TypeScript types exported by the SDK packages.

FlagResult

All evaluation methods return a FlagResult — not a plain boolean.

interface FlagResult {
value: boolean | string | number;
reason: string; // 'TARGETING_MATCH' | 'DEFAULT' | 'DISABLED' | ...
}

EvaluationContext

Passed to identify(), evaluate(), and the provider's context prop.

interface EvaluationContext {
userId: string; // Required
[key: string]: string | number | boolean | string[] | undefined; // Custom attributes
}

EvaluationReason

type EvaluationReason =
| 'TARGETING_MATCH' // Matched a targeting rule
| 'DEFAULT' // Returned the default value
| 'DISABLED' // Flag is disabled
| 'PREREQUISITE_FAILED' // A prerequisite flag check failed
| 'SCHEDULE_INACTIVE' // Outside the flag's active schedule
| 'ARCHIVED' // Flag has been archived
| 'ERROR'; // Evaluation error

FlagLayerError

Thrown by SDK methods on API errors.

import { FlagLayerError } from '@flaglayer/sdk';
try {
await fl.evaluate('flag', ctx);
} catch (e) {
if (e instanceof FlagLayerError) {
console.log(e.code); // Error code string
console.log(e.status); // HTTP status number
}
}

API Reference

All SDK endpoints require an API key in the x-api-key header.

Evaluation

POST /v1/evaluate

Evaluate a single flag.

// Request
{
"flagKey": "new-feature",
"context": { "userId": "user-123", "email": "user@example.com" }
}
// Response
{ "value": true, "reason": "TARGETING_MATCH" }

POST /v1/evaluate-all

Evaluate all flags for a user.

// Request
{
"context": { "userId": "user-123" },
"prefix": "exp_"
}
// Response
{
"flags": {
"exp_new-dashboard": { "value": true, "reason": "TARGETING_MATCH" },
"exp_dark-mode": { "value": false, "reason": "DEFAULT" }
}
}

Admin

Admin endpoints use session authentication (used by the dashboard). These are available for building custom integrations.

ResourceEndpoints
ProjectsGET POST PATCH DELETE /v1/admin/projects
FlagsGET POST PATCH DELETE /v1/admin/flags
Flag RulesGET PATCH /v1/admin/flags/:flagId/rules/:envId
EnvironmentsGET POST /v1/admin/environments
API KeysGET POST DELETE /v1/admin/api-keys
UsersGET /v1/admin/users · PATCH /:id/role · DELETE /:id
InvitationsPOST /v1/admin/invitations · POST /:id/resend

Error Responses

{ "statusCode": 401, "message": "Invalid API key" }
{ "statusCode": 429, "message": "Rate limit exceeded" }
{ "statusCode": 404, "message": "Flag not found" }

Evaluation endpoints are rate-limited per API key. Include your API key in the x-api-key header with every request.