Skip to content

Client SDKs

The EdgeFlags client SDKs provide a type-safe, real-time interface for reading feature flags and configs. The core SDK (@edgeflags/sdk) works in any JavaScript runtime, and the React package (@edgeflags/react) adds hooks and a context provider.

Both packages use the bulk evaluation endpoint under the hood — flags and configs are fetched in a single request and cached locally for synchronous reads.

Installation

Terminal window
npm install @edgeflags/sdk
# React bindings (optional)
npm install @edgeflags/react

Quick start

import { EdgeFlags } from '@edgeflags/sdk';
const ef = new EdgeFlags({
token: 'ff_production_abc123',
baseUrl: 'https://edgeflags.net',
context: {
user_id: 'u_42',
plan: 'premium',
custom: { country: 'US' },
},
});
await ef.init();
const darkMode = ef.flag('dark_mode', false);
const limits = ef.config('api_limits', { requests_per_minute: 100 });

After init() resolves, all reads are synchronous from the local cache. The SDK polls for updates in the background.

Configuration

Pass these options to the EdgeFlags constructor:

OptionTypeDefaultDescription
tokenstringrequiredBearer token for authentication
baseUrlstringrequiredEdgeFlags API URL
contextEvaluationContextundefinedUser context for targeting evaluation
pollingIntervalnumber60000Background poll interval in milliseconds
bootstrapobjectundefinedInitial flag/config values for instant reads before init()
debugbooleanfalseLog SDK activity to the console

Evaluation context

The context object is sent with every evaluation request and drives targeting rules:

interface EvaluationContext {
user_id?: string;
email?: string;
phone?: string;
plan?: string;
segments?: string[];
environment?: string;
custom: Record<string, unknown>;
}

Reading flags

Use flag() to read a flag value. Pass a default as the second argument — its type narrows the return type.

// Returns boolean | string | number | object | undefined
const raw = ef.flag('dark_mode');
// Returns boolean (default provides type + fallback)
const darkMode = ef.flag('dark_mode', false);
// String flags
const banner = ef.flag('banner_text', '');
// Number flags
const limit = ef.flag('request_limit', 1000);
// JSON flags
const theme = ef.flag('theme_override', { primary: '#000' });

Use allFlags() to get every cached flag as a Record<string, FlagValue>.

Reading configs

Use config() the same way. The generic parameter provides type safety:

const providers = ef.config<PaymentConfig>('payment_providers', {
stripe_enabled: true,
paypal_enabled: false,
});
const all = ef.allConfigs();

Identifying users

Call identify() to update the evaluation context and immediately refresh all values:

await ef.identify({
user_id: 'u_99',
email: 'jane@example.com',
plan: 'enterprise',
custom: { beta_enrolled: true },
});
// Flags and configs now reflect the new user

Events

The SDK emits three events:

EventPayloadWhen
readyundefinedinit() completes successfully
changeChangeEventFlag or config values change after a poll
errorErrorNetwork or API error during polling
// Subscribe — returns an unsubscribe function
const off = ef.on('change', (event) => {
for (const { key, previous, current } of event.flags) {
console.log(`Flag ${key}: ${previous}${current}`);
}
});
ef.on('error', (err) => {
console.error('EdgeFlags polling error:', err);
});
// Unsubscribe
off();

Bootstrap and offline fallback

Provide bootstrap values so flags are available immediately, before the first network request:

const ef = new EdgeFlags({
token: 'ff_production_abc123',
baseUrl: 'https://edgeflags.net',
bootstrap: {
flags: { dark_mode: false, new_checkout: true },
configs: { api_limits: { requests_per_minute: 500 } },
},
});
// Available immediately — no await needed
const darkMode = ef.flag('dark_mode', false);
// init() will replace bootstrap values with live data
await ef.init();

React

The @edgeflags/react package provides a context provider and hooks that trigger re-renders when values change.

Provider setup

Wrap your app with EdgeFlagsProvider. You can either pass configuration props or an existing client instance:

import { EdgeFlagsProvider } from '@edgeflags/react';
function App() {
return (
<EdgeFlagsProvider
token="ff_production_abc123"
baseUrl="https://edgeflags.net"
context={{
user_id: currentUser.id,
plan: currentUser.plan,
custom: {},
}}
>
<YourApp />
</EdgeFlagsProvider>
);
}

Or with an external client (useful when you need access to the client outside React):

import { EdgeFlags } from '@edgeflags/sdk';
import { EdgeFlagsProvider } from '@edgeflags/react';
const client = new EdgeFlags({ token, baseUrl, context });
await client.init();
function App() {
return (
<EdgeFlagsProvider client={client}>
<YourApp />
</EdgeFlagsProvider>
);
}

useFlag

Read a flag value. The component re-renders when the value changes.

import { useFlag } from '@edgeflags/react';
function Checkout() {
const newCheckout = useFlag('new_checkout', false);
return newCheckout ? <NewCheckoutFlow /> : <LegacyCheckout />;
}

useConfig

Read a config value with the same re-render behavior.

import { useConfig } from '@edgeflags/react';
function PaymentForm() {
const config = useConfig<PaymentConfig>('payment_providers', {
stripe_enabled: true,
paypal_enabled: false,
});
return config.stripe_enabled ? <StripeForm /> : null;
}

useEdgeFlags

Access the underlying EdgeFlags client for manual operations like identify() or refresh().

import { useEdgeFlags } from '@edgeflags/react';
function LoginHandler() {
const ef = useEdgeFlags();
async function onLogin(user: User) {
await ef.identify({
user_id: user.id,
email: user.email,
plan: user.plan,
custom: {},
});
}
return <LoginForm onSuccess={onLogin} />;
}

Testing

Use createMockClient() to create a client that returns static values without making network requests:

import { createMockClient } from '@edgeflags/sdk';
const mock = createMockClient({
flags: { dark_mode: true, new_checkout: false },
configs: { api_limits: { requests_per_minute: 9999 } },
});
// Works like a real client
mock.flag('dark_mode', false); // true
mock.config('api_limits'); // { requests_per_minute: 9999 }

In React tests, pass the mock client to the provider:

import { createMockClient } from '@edgeflags/sdk';
import { EdgeFlagsProvider } from '@edgeflags/react';
const mock = createMockClient({ flags: { dark_mode: true } });
render(
<EdgeFlagsProvider client={mock}>
<ComponentUnderTest />
</EdgeFlagsProvider>
);

Cleanup

Call destroy() to stop polling and release resources:

ef.destroy();

In React, the provider handles cleanup automatically when it unmounts. If you passed an external client, you are responsible for calling destroy() yourself.