Flaggy
Get started
On this page
JavaScript & TypeScript

SDK Documentation

Everything you need to install, configure, and evaluate feature flags with the Flaggy SDK.

Installation

Install the SDK from npm. One package covers both browser and Node.js environments.

npm install @flaggy.io/sdk-js

Requirements

  • Node.js 18.0.0 or higher
  • Browser: any modern browser with ES2020 support

Quick Start

Three steps to evaluate your first flag.

1

Create the client

import { flaggy } from "@flaggy.io/sdk-js";

export const flagClient = flaggy({
  apiKey: process.env.FLAGGY_API_KEY!,
});
2

Initialize before use

await flagClient.initialize();

Ensures flags are loaded before evaluation. On Node.js it waits for the first fetch. In the browser it returns immediately if a warm cache exists.

3

Check a flag

if (flagClient.isEnabled("new-checkout", { key: "user-123" })) {
  // show new checkout
}

Setup by Environment

React / Browser

// src/lib/flaggy.ts
import { flaggy } from "@flaggy.io/sdk-js";

export const flagClient = flaggy({
  apiKey: import.meta.env.VITE_FLAGGY_API_KEY!,
  environment: import.meta.env.VITE_ENVIRONMENT,
});
// src/main.tsx
await flagClient.initialize();

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <App />
  </StrictMode>,
);
if (flagClient.isEnabled("new-header", { key: user.id, plan: user.plan })) {
  return <NewHeader />;
}

Node.js

// src/lib/flaggy.ts
import { flaggy } from "@flaggy.io/sdk-js";

export const flagClient = flaggy({
  apiKey: process.env.FLAGGY_API_KEY!,
  environment: process.env.NODE_ENV,
});
// src/server.ts
await flagClient.initialize();

app.listen(3000);
if (flagClient.isEnabled("new-algorithm", { key: req.user.id, plan: req.user.plan })) {
  // use new algorithm
}

Configuration

Options passed to flaggy() when creating the client.

const flagClient = flaggy({
  apiKey: "your-api-key",     // required
  environment: "production",  // optional — defaults to "production"
  onError: (err) => {         // optional — called on fetch failure
    console.warn(err);
  },
});
Option Type Default Description
apiKey string Required. Your Flaggy API key.
environment string "production" Target environment. Accepts production, staging, or development. Any other value falls back to production.
onError (err) => void Optional. Called on fetch failure. Does not throw.
Singleton. Calling flaggy() across multiple modules returns the same instance. Safe to call from anywhere — only one client is created.

Initialization

Call client.initialize() once at application startup before evaluating any flags.

Browser

Flags are cached in localStorage. If a warm cache exists, initialize() resolves immediately and triggers a background refresh. If not, it waits for the first fetch.

Node.js

Cached in-memory per process. The cache is empty on restart, so initialize() always waits for the first fetch before resolving. Call it before accepting requests.

Checking Flags

isEnabled evaluates a flag and returns a boolean. Evaluation is local — no network call happens at the point of evaluation.

// Basic on/off flag
client.isEnabled("my-flag");

// With context for targeting
client.isEnabled("my-flag", { key: "user-123", plan: "pro" });

// With a custom default
client.isEnabled("my-flag", { key: "user-123" }, false);

If the flag doesn't exist, isEnabled returns defaultValue (default: false). Flags with no segments return true when enabled — they are global on/off switches.

Context Object

The context is a flat key-value object describing the entity being evaluated. It determines which segments the entity belongs to.

The key field is required

It must be a stable string identifier for the entity — a user ID, company slug, device ID, or session ID. It is used for deterministic rollout bucketing and is hashed before storage.

User context

flagClient.isEnabled("new-dashboard", {
  key: "user-123",   // required — stable entity identifier
  plan: "pro",
  country: "US",
  betaOptIn: true,
});

Company context

flagClient.isEnabled("enterprise-feature", {
  key: "acme-corp",
  plan: "enterprise",
  company_size: 500,
});

Device context

flagClient.isEnabled("new-onboarding", {
  key: "device-abc123",
  platform: "ios",
  app_version: "2.1.0",
});

All attribute values are coerced to strings before comparison. If an attribute is missing from the context, conditions that reference it evaluate to false.

Segment Targeting

Segments let you enable a flag only for entities that match specific attributes. Each segment has a rollout percentage and a set of conditions.

  • All conditions within a segment must match — AND logic. e.g. plan equals "pro" AND country equals "US"
  • Segments are evaluated in priority order — the first matching segment wins.
  • Rollout percentage is deterministic — the same entity always gets the same result (bucketed via MurmurHash3 on the key).

Condition operators

Operator Description
equals Attribute exactly matches the value
not_equals Attribute does not match the value
contains Attribute string contains the value
not_contains Attribute string does not contain the value
starts_with Attribute string starts with the value
ends_with Attribute string ends with the value

Evaluation Logic

When you call isEnabled, the SDK evaluates the flag in this order:

Step Condition Result
1 Flag not found defaultValue (default: false)
2 Flag is disabled false
3 Flag has no segments true — global on/off flag
4 Segments defined, no context passed false
5 Context passed, no segment matches false
6 Segment matches, entity outside rollout false
7 Segment matches, entity inside rollout true
If a flag has segments but you don't pass a context, it always returns false. Always pass a context when evaluating targeted flags.

Analytics

The SDK automatically records flag evaluations and flushes them to Flaggy in the background. No configuration required.

Batched flushing

Events are sent every 5 seconds, or immediately when 100 events accumulate.

Per-session deduplication

Each unique combination of flag, entity key, and result is recorded once per session. Re-renders don't produce duplicates.

Page unload

In the browser, buffered events are flushed when the page unloads via navigator.sendBeacon.

Non-blocking

Analytics never block flag evaluation. Errors are swallowed silently.

Event shape

{
  "flag_key": "new-feature",
  "result": true,
  "segment_matched": "beta-users",
  "context_key": "user-123"   // hashed before storage
}

segment_matched is the key of the matching segment, or "no_match" if none matched. context_key is your entity key, hashed before storage.

Caching & Refresh

The SDK caches flags locally and refreshes them automatically in the background.

Browser

Stored in localStorage. Loaded immediately on startup; a stale cache triggers a background refresh without blocking evaluation.

Node.js

Cached in-memory per process. Empty on restart — initialize() always fetches before resolving.

Backoff on failure. The refresh interval starts at 60 seconds and doubles on each failure, capped at 15 minutes. Request timeouts (3 seconds) also count as failures — this intentionally backs clients off during outages to aid recovery.

API Reference

flaggy(config): FeatureFlagClient

Creates (or returns) the global singleton client. Safe to call across modules — subsequent calls with the same API key return the same instance.

Param Type Description
apiKey string Required. Your project API key.
environment string Optional. Defaults to production.
onError (err) => void Optional. Called on fetch failure, does not throw.
client.initialize(): Promise<void>

Ensures flags are ready. Call once at application startup before evaluating any flags. Resolves when the flag ruleset is available locally.

client.isEnabled(flagName, context?, defaultValue?): boolean

Evaluates a flag locally. Returns defaultValue (false) if the flag doesn't exist. The key field in context is required when passing a context.

client.isEnabled(flagName: string, context?: object, defaultValue?: boolean): boolean
client.getAllFlags(): Record<string, FeatureFlag>

Returns a shallow copy of all currently cached flags. Useful for debugging or rendering a flag state panel.

const flags = client.getAllFlags();
// Returns: Record<string, FeatureFlag> — shallow copy of the cache

Ready to get started?

Create a free account and have your first flag running in minutes.

Get started free →