Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.flagmint.com/llms.txt

Use this file to discover all available pages before exploring further.

Flagmint JavaScript SDK

The Flagmint JavaScript SDK is a framework-agnostic client for evaluating feature flags with pluggable caching and flexible transport strategies. It works seamlessly in both browser and Node.js server-side environments.

Quick Start

Get up and running in minutes

React Integration

Framework-specific examples

Configuration

Customize caching and transport

API Reference

Complete method documentation

Key Features

  • Framework-Agnostic — Works with React, Vue, vanilla JS, Node.js, and more
  • Flexible Transport — WebSocket for real-time updates with automatic long-polling fallback
  • Pluggable Caching — Built-in sync cache, async cache (Redis/filesystem), or custom implementations
  • Server & Browser Support — Compatible with browser, Node.js, and React Native
  • Type-Safe — Full TypeScript support with comprehensive type definitions
  • Zero-Config Defaults — Works out of the box with sensible defaults

Installation

npm install flagmint-js-sdk
TypeScript Support: The SDK includes built-in TypeScript declarations with full type safety.

Quick Start

import { FlagClient } from 'flagmint-js-sdk';

const client = new FlagClient({
  apiKey: 'ff_your_api_key_here',
  context: {
    kind: "multi",
    user: {
      kind: "user",
      key: 'user123',
      email: 'user@example.com'
    },
    organization: {
      kind: "organization",
      key: 'org456',
      plan: 'premium'
    }
  }
});

// Wait for initial connection and flag sync
try {
  await client.ready();
} catch (err) {
  // Transport failed — flags may still be available from cache
  console.warn('Connection failed:', err.message);
  console.log('Cached flags:', client.getFlags());
}

// Get flag values
const showBanner = client.getFlag('show_banner', false);
const featureVersion = client.getFlag('feature_version', 'v1');

// Update context and re-evaluate (returns a Promise)
await client.updateContext({
  user: {
    key: 'user456',
    email: 'newuser@example.com'
  }
});
ready() rejects on transport failure. If the SDK cannot connect to Flagmint, await client.ready() will throw. If offline caching is enabled (which it is by default), cached flags may still be available via client.getFlags() — wrap ready() in a try/catch to handle this gracefully. See Error Handling for details.

Configuration

FlagClientOptions

OptionTypeDefaultDescription
apiKeystringRequiredYour environment API key
contextRecord<string, any>{}Initial evaluation context (user attributes, org, etc.)
enableOfflineCachebooleantrueCache flags locally for offline resilience
persistContextbooleanfalsePersist evaluation context across sessions
cacheAdapterCacheAdapterSync helperCustom cache implementation
transportMode'auto' | 'websocket' | 'long-polling''auto'Transport strategy for flag updates
onError(error: Error) => voidundefinedCallback for transport or initialization errors
previewModebooleanfalseEvaluate using rawFlags only, bypassing remote fetch
rawFlagsRecord<string, FlagValue>undefinedLocal-only flag definitions for preview mode
deferInitializationbooleanfalseIf true, initialization is deferred until ready() is called
restEndpointstringAuto-detectedCustom REST evaluation endpoint
wsEndpointstringAuto-detectedCustom WebSocket endpoint
Endpoint auto-detection: The SDK automatically selects endpoints based on NODE_ENV — production uses api.flagmint.com, staging uses staging-api.flagmint.com, and development defaults to localhost:3000. Override with restEndpoint and wsEndpoint if needed.

Context Structure

The context object should follow this structure:
{
  kind: "multi" | "user" | "organization",
  user?: {
    kind: "user",
    key: string,        // Unique user identifier
    email?: string,
    name?: string,
    // ... any custom attributes
  },
  organization?: {
    kind: "organization",
    key: string,        // Unique org identifier
    plan?: string,
    // ... any custom attributes
  }
}

Cache Adapters

Sync (Browser / In-Memory)

By default, the SDK uses a sync localStorage-based helper in browsers or a Map in Node.js:
import { FlagClient } from 'flagmint-js-sdk';
import { setCacheStorage } from 'flagmint-js-sdk/core/cacheHelper';

// For Node.js, override with custom storage
const myMap = new Map();
setCacheStorage({
  getItem: (key) => myMap.get(key) ?? null,
  setItem: (key, val) => myMap.set(key, val),
});

const client = new FlagClient({
  apiKey: 'ff_...',
  enableOfflineCache: true
});

Async (Redis / File System)

Use the async helper for server-side or React Native:
import { FlagClient } from 'flagmint-js-sdk';
import * as asyncCache from 'flagmint-js-sdk/core/cacheHelper.async';

const client = new FlagClient({
  apiKey: 'ff_...',
  cacheAdapter: {
    loadFlags: asyncCache.loadCachedFlags,
    saveFlags: asyncCache.saveCachedFlags,
    loadContext: asyncCache.loadCachedContext,
    saveContext: asyncCache.saveCachedContext
  }
});

Custom Cache (Redis Example)

import { FlagClient } from 'flagmint-js-sdk';
import redis from 'redis';

const redisClient = redis.createClient();

const client = new FlagClient({
  apiKey: 'ff_...',
  cacheAdapter: {
    loadFlags: async (apiKey, cacheTTL) => {
      const cached = await redisClient.get(`flags:${apiKey}`);
      if (!cached) return null;
      const parsed = JSON.parse(cached);
      // Optionally respect cacheTTL (in ms) for expiry logic
      return parsed;
    },
    saveFlags: async (apiKey, flags) => {
      await redisClient.setex(
        `flags:${apiKey}`,
        600, // 10 min TTL
        JSON.stringify(flags)
      );
    },
    loadContext: async (apiKey) => {
      const cached = await redisClient.get(`context:${apiKey}`);
      return cached ? JSON.parse(cached) : null;
    },
    saveContext: async (apiKey, context) => {
      await redisClient.setex(
        `context:${apiKey}`,
        3600, // 1 hour TTL
        JSON.stringify(context)
      );
    }
  }
});
The default cache TTL is 24 hours. The loadFlags function receives the TTL (in milliseconds) as a second argument so your custom adapter can respect it.

Transport Modes

The SDK supports three transport strategies:
ModeLatencyResource UsageBest For
WebSocketLowest (real-time)EfficientReal-time dashboards, live updates
Long-PollingMediumHigherSimpler setup, firewall-friendly
Auto (default)Lowest to MediumVariesMost applications (recommended)
const client = new FlagClient({
  apiKey: 'ff_...',
  transportMode: 'websocket' // or 'long-polling' or 'auto'
});

Transport Behavior

  • WebSocket: Persistent connection for instant flag updates
  • Long-Polling: Polls at 20-minute intervals with exponential backoff on failure (up to 60s max, 2x multiplier)
  • Auto (default): Tries WebSocket first, falls back to long-polling if the WebSocket connection fails

Framework Integration Examples

React

import { useEffect, useState } from 'react';
import { FlagClient } from 'flagmint-js-sdk';

const client = new FlagClient({
  apiKey: 'ff_...',
  context: {
    kind: "multi",
    user: { kind: "user", key: 'user123', email: 'user@example.com' }
  }
});

export function App() {
  const [flags, setFlags] = useState({});
  const [ready, setReady] = useState(false);

  useEffect(() => {
    // Subscribe to flag changes
    const unsubscribe = client.subscribe((updatedFlags) => {
      setFlags({
        showBeta: updatedFlags['show_beta'] ?? false,
        theme: updatedFlags['theme'] ?? 'light'
      });
    });

    // Initialize
    client.ready()
      .then(() => setReady(true))
      .catch((err) => {
        console.warn('Connection failed, using cached flags:', err.message);
        setReady(true); // flags may already be set from cache via subscribe
      });

    return () => {
      unsubscribe();
    };
  }, []);

  if (!ready) return <div>Loading...</div>;

  return (
    <div className={`theme-${flags.theme}`}>
      {flags.showBeta && <BetaFeature />}
    </div>
  );
}
The subscribe() callback fires immediately with current flags (including cached flags), so your UI is populated before ready() even resolves.

Node.js / Express

import { FlagClient } from 'flagmint-js-sdk';
import * as asyncCache from 'flagmint-js-sdk/core/cacheHelper.async';

const client = new FlagClient({
  apiKey: 'ff_...',
  cacheAdapter: {
    loadFlags: asyncCache.loadCachedFlags,
    saveFlags: asyncCache.saveCachedFlags,
    loadContext: asyncCache.loadCachedContext,
    saveContext: asyncCache.saveCachedContext
  },
  onError: (err) => console.error('Flagmint error:', err)
});

try {
  await client.ready();
} catch (err) {
  console.warn('Flagmint unavailable, continuing with cached flags');
}

app.get('/api/feature', async (req, res) => {
  const isEnabled = client.getFlag('new_api', false);
  res.json({ enabled: isEnabled });
});

Vanilla JavaScript

import { FlagClient } from 'flagmint-js-sdk';

const client = new FlagClient({
  apiKey: 'ff_...',
  context: {
    kind: "user",
    user: { kind: "user", key: 'user123' }
  }
});

try {
  await client.ready();
} catch (err) {
  console.warn('Using cached flags');
}

const showNewUI = client.getFlag('new_ui', false);
if (showNewUI) {
  document.getElementById('app').classList.add('new-ui');
}

Context Management

Updating Context

Update user context at any time to re-evaluate flags:
const client = new FlagClient({
  apiKey: 'ff_...',
  context: {
    kind: "user",
    user: { kind: "user", key: 'user123' }
  }
});

// Update context later (e.g., after login)
// This triggers a re-fetch of flags with the new context
await client.updateContext({
  kind: "multi",
  user: {
    kind: "user",
    key: 'user123',
    email: 'user@example.com',
  },
  organization: {
    kind: "organization",
    key: 'org456',
    plan: 'premium',
    country: 'CA'
  }
});
updateContext() merges the new context with the existing context and re-fetches flags from the server. If the transport is unavailable, the context is still updated locally but flags will not reflect the change until the connection is restored.

Persisting Context

Enable context persistence across sessions:
const client = new FlagClient({
  apiKey: 'ff_...',
  persistContext: true,
  context: {
    kind: "user",
    user: { kind: "user", key: 'user123' }
  }
});

Deferred Initialization

For scenarios where you need to set up the client before context is available:
const client = new FlagClient({
  apiKey: 'ff_...',
  deferInitialization: true
});

// Later, when context is ready (e.g., after authentication)
await client.updateContext({
  kind: "user",
  user: { kind: "user", key: 'user123', email: 'user@example.com' }
});

// Calling ready() triggers initialization when deferInitialization is true
try {
  await client.ready();
} catch (err) {
  console.warn('Connection failed:', err.message);
}

Error Handling

The SDK provides two mechanisms for handling errors:

onError callback

Use the onError option to receive errors without wrapping ready() in a try/catch:
const client = new FlagClient({
  apiKey: 'ff_...',
  onError: (error) => {
    console.error('Flagmint error:', error);
    // Report to monitoring service
    sentry.captureException(error);
  }
});

ready() rejection

ready() always rejects on transport failure, regardless of whether onError is set. Wrap it in a try/catch to prevent unhandled promise rejections:
try {
  await client.ready();
} catch (err) {
  console.warn('Connection failed:', err.message);

  // Cached flags may still be available
  const flags = client.getFlags();
  if (Object.keys(flags).length > 0) {
    console.log('Continuing with cached flags');
  } else {
    console.error('No cached flags available — flags will return fallback values');
  }
}
Always wrap ready() in a try/catch. If the Flagmint API is unreachable, ready() will reject. Without a try/catch, this becomes an unhandled promise rejection that may crash your application. When enableOfflineCache is true (the default), cached flags are loaded before the transport connects, so getFlag() and getFlags() will return cached values even after ready() rejects.

Offline resilience summary

Scenarioready()getFlags()onError
Transport succeedsResolvesFresh flagsNot called
Transport fails, cache existsRejectsCached flagsCalled
Transport fails, no cacheRejects{} (empty)Called
Transport fails, cache expired (>24h)Rejects{} (empty)Called

Subscribing to flag updates

Use subscribe() to react to flag changes in real time:
const unsubscribe = client.subscribe((flags) => {
  console.log('Flags updated:', flags);
  // Update your UI, re-render components, etc.
});

// The callback fires immediately with current flags,
// then again whenever flags change via the transport.

// Unsubscribe when done
unsubscribe();

Preview Mode

For testing or preview environments where you want to bypass remote flag fetching:
const client = new FlagClient({
  apiKey: 'ff_...',
  previewMode: true,
  rawFlags: {
    new_checkout: true,
    discount_percent: 20,
    ui_variant: 'experimental'
  }
});

// No network calls — flags are evaluated locally
const checkout = client.getFlag('new_checkout', false); // true
const discount = client.getFlag('discount_percent', 0); // 20
In preview mode, the client is immediately ready — no ready() call is needed (though calling it is safe). Flags are evaluated locally against the provided context using the SDK’s built-in evaluation engine.

API Reference

FlagClient Methods

ready(timeoutMs?: number): Promise<void>

Wait for the initial connection and flag synchronisation to complete. Rejects if the transport fails. Default timeout is 3000ms.
try {
  await client.ready();       // 3s default timeout
  await client.ready(5000);   // custom 5s timeout
} catch (err) {
  console.warn('Connection failed:', err.message);
}
When deferInitialization is true, calling ready() triggers initialization.

getFlags(): FeatureFlags<T>

Get all current flag values as a shallow copy.
const allFlags = client.getFlags();

getFlag<K>(key: K, fallback?: T): T

Get a single flag value with an optional fallback.
const theme = client.getFlag('theme', 'light');
const showFeature = client.getFlag('new_feature', false);

updateContext(context: C): Promise<void>

Merge new context with the existing context. Triggers a re-fetch of flags from the server.
await client.updateContext({
  user: { key: 'user456', email: 'newuser@example.com' }
});

subscribe(callback: (flags: FeatureFlags<T>) => void): () => void

Subscribe to flag changes. The callback fires immediately with current flags, then on every subsequent update. Returns an unsubscribe function.
const unsubscribe = client.subscribe((flags) => {
  console.log('Current flags:', flags);
});

// Later
unsubscribe();

destroy(): void

Close the transport connection, clear all subscribers, and clean up resources.
client.destroy();

CacheAdapter Interface

interface CacheAdapter<C = any> {
  loadFlags(apiKey: string, cacheTTL: number): Promise<Record<string, any>> | Record<string, any>;
  saveFlags(apiKey: string, flags: Record<string, any>): Promise<void> | void;
  loadContext(apiKey: string): Promise<C | null> | C | null;
  saveContext(apiKey: string, context: C): Promise<void> | void;
}

Evaluation Helpers

The SDK includes low-level evaluation utilities:

evaluateFlagValue(flag, context)

import { evaluateFlagValue } from 'flagmint-js-sdk/core/evaluation';

const result = evaluateFlagValue(flagConfig, userContext);
Applies targeting rules (segment or attribute rules) and rollouts.

evaluateRollout(rollout, context)

import { evaluateRollout } from 'flagmint-js-sdk/core/evaluation';

const rolloutValue = evaluateRollout(rolloutConfig, userContext);
Supports percentage rollouts with consistent hashing and variant distributions (A/B tests).

isInSegment(context, segment)

import { isInSegment } from 'flagmint-js-sdk/core/evaluation';

const inBetaSegment = isInSegment(userContext, betaSegment);
Evaluates whether a user context matches a segment’s criteria.

rolloutUtils

import { rolloutUtils } from 'flagmint-js-sdk/core/evaluation';

const percentage = rolloutUtils.hashToPercentage('user123');
const variant = rolloutUtils.pickVariant('user123', variantConfig);
Utilities for consistent hashing and variant assignment.

SDK Tester Tool

Use the Flagmint SDK Tester for interactive testing:
git clone https://github.com/jtad009/flagmint-sdk-tester.git
cd flagmint-sdk-tester
npm install
npm start
Features include dual transport testing (WebSocket + HTTP), a visual context builder, protocol logging and debugging, rollout simulation, and performance monitoring.

Troubleshooting

Client not connecting

  • Verify your API key is correct
  • Check network connectivity
  • Review browser console for errors
  • Ensure CORS is properly configured if using from browser

Flags not updating

  • Check transport mode (WebSocket vs long-polling)
  • Verify context is properly set with updateContext()
  • Use subscribe() to listen for flag changes
  • If using a custom cache adapter, verify your TTL isn’t too aggressive

ready() keeps rejecting

  • Check that the Flagmint API is reachable from your environment
  • If behind a firewall, try transportMode: 'long-polling' (WebSocket may be blocked)
  • Wrap ready() in a try/catch and fall back to cached flags — see Error Handling

Performance issues

  • Consider using long-polling instead of WebSocket for high-traffic scenarios
  • Implement proper cache TTLs with a custom cache adapter
  • Use Redis caching for server-side applications

TypeScript errors

  • Ensure you’re using the latest version of the SDK
  • Check that your tsconfig.json includes proper module resolution
  • Use explicit type parameters: client.getFlag<boolean>('flag', false)

Support & Resources

Documentation

Full platform documentation

GitHub Issues

Report bugs and issues

Email Support

Contact our support team

SDK Tester

Interactive testing tool

License

MIT © Flagmint