> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flagsync.com/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript SDK

> Integrate FlagSync feature flags into web applications.

## Overview

The `@flagsync/js-sdk` integrates into web applications for client-side feature management and event tracking—ideal for single-user contexts like browser environments.

<CardGroup cols={2}>
  <Card title="npm" icon="npm" href="https://www.npmjs.com/package/@flagsync/js-sdk">
    Latest version available on npm
  </Card>

  <Card title="GitHub" icon="github" href="https://github.com/flagsync/js-sdk/">
    Explore the source code on GitHub
  </Card>
</CardGroup>

## Installation

Install the SDK with your preferred package manager:

<CodeGroup>
  ```bash npm theme={null}
  npm install @flagsync/js-sdk
  npm install -D @flagsync/cli
  ```

  ```bash yarn theme={null}
  yarn add @flagsync/js-sdk
  yarn add -D @flagsync/cli
  ```

  ```bash pnpm theme={null}
  pnpm add @flagsync/js-sdk
  pnpm add -D @flagsync/cli
  ```
</CodeGroup>

## Quickstart

A basic example of using the SDK in a JavaScript application:

```typescript theme={null}
import { FlagSyncFactory } from '@flagsync/js-sdk';

// Initialize the SDK
const factory = FlagSyncFactory({
  sdkKey: 'your-sdk-key',
  core: {
    key: 'user-123', // Unique identifier
    attributes: {
      plan: 'premium',
      country: 'US'
    }
  }
});

// Get the client
const client = factory.client()

// Wait for SDK to be ready
await client.waitForReady();

// Evaluate a flag
const isFeatureEnabled = client.flag('my-first-kill-switch');
```

## Initialization

### Get Your SDK Key

Find your client-side SDK key in your [workspace settings](https://www.flagsync.com/dashboard/settings/organization/workspaces/)—safe for web/mobile apps (keep server-side keys private).

### Initialize the SDK

Initialize the SDK with your SDK key and a user context (e.g., user ID):

```typescript theme={null}
import { FlagSyncFactory } from '@flagsync/js-sdk';

const factory = FlagSyncFactory({
  sdkKey: 'your-sdk-key',
  core: {
    key: 'user123', // Unique identifier
    attributes: {
      email: 'user@example.com'
    }
  }
});

const client = factory.client();
```

<Warning>
  Ensure the `key` in `FsUserContext` is unique and persistent for accurate MAU tracking and consistent flag evaluations. See [User Context Best Practices](/sdks/overview#user-context-best-practices) for details.
</Warning>

### Wait for Readiness

Use events or promises to ensure the SDK is ready before evaluating flags.

Always wait for SDK initialization before evaluating any flags. Initialization usually completes within 15–30ms, depending on flag count and ruleset complexity.

<Steps>
  <Step title="Promises">
    <CodeGroup>
      ```typescript async/await theme={null}
      await client.waitForReady();
      // SDK is ready
      ```

      ```typescript async/await (throws) theme={null}
      // Throw an error if the SDK fails to initialize.
      try {
        await client.waitForReadyCanThrow();
        // SDK is ready
      } catch (error) {
        // Handle initialization error
      }
      ```
    </CodeGroup>
  </Step>

  <Step title="Events">
    <CodeGroup>
      ```typescript SDK_READY theme={null}
      import { FsEvent } from '@flagsync/js-sdk';

      client.once(FsEvent.SDK_READY, () => {
        // SDK is ready
      });
      ```

      ```typescript SDK_READY_FROM_STORE theme={null}
      import { FsEvent } from '@flagsync/js-sdk';

      client.once(FsEvent.SDK_READY_FROM_STORE, () => {
        // Emitted once the SDK initializes from LocalStorage
        // Fires quickly—no network request required
        // Data may be stale, not a replacement for SDK_READY
      });
      ```
    </CodeGroup>

    <Info>
      This event only fires when using the `localstorage` storage type; the default is `memory`.
    </Info>
  </Step>
</Steps>

#### SDK Not Ready

If the SDK isn’t ready, it returns the `defaultValue` or `control`:

```typescript theme={null}
// SDK not ready
client.flag('flag-one', false); // false (defaultValue)
client.flag('flag-two');        // "control"
```

#### SDK Ready

Once ready, `flag()` returns the server-evaluated value:

```typescript theme={null}
await client.waitForReady();
const value = client.flag('flag-one'); // Server-evaluated value
```

<Info>
  * Client-side SDKs can bootstrap via `LocalStorage` or an initial flag set—values apply until the SDK is ready.
  * See [Flag Evaluation: Overview](/sdks-flag-evaluation/overview).
</Info>

## Usage

### Evaluate Flags

Evaluates a feature flag for the user context. Applies targeting rules, rollouts, and defaults values.

```typescript theme={null}
client.flag<T>(flagKey: string, defaultValue?: T): T;
```

#### FlagSync CLI

When using the CLI, flag values are automatically inferred from the generated types:

```typescript theme={null}
client.flag('layout');              // Type: 'v1' | 'v2' | 'v3'
client.flag('killswitch');          // Type: boolean
client.flag('price-discount');      // Type: 0.1 | 0.2
client.flag('price-discount', 0.5); // ❌ TS Error: Invalid default value
client.flag('invalid-flag');        // ❌ TS Error: Invalid flag key
```

Without generated types, you **must** manually specify the type:

```typescript theme={null}
client.flag<'v1' | 'v2'>('layout');            // Type: 'v1' | 'v2'
client.flag<number>('price-discount');         // Type: number
client.flag<boolean>('enable-feature', false); // Type: boolean (defaultValue must be true or false)
client.flag('no-type')                         // Type: unknown
```

<Tip>
  An [Impression](/impressions/generating) is automatically registered when `flag()` is called.
</Tip>

### Track Events

Submit user actions with `track()`—use `eventValue` for numeric data or `properties` for rich context:

```typescript theme={null}
client.track(
  eventKey: string,
  eventValue?: number | null,
  properties?: Record<string, any> | null
);
```

There are two types of events:

<CardGroup>
  <Card title="Numeric Events" icon="hashtag" href="/events/tracking#numeric-events">
    Captures measurable data like time or counts.
  </Card>

  <Card title="Property Events" icon="brackets-curly" href="/events/tracking#property-events">
    Captures rich context like purchase details.
  </Card>
</CardGroup>

<CodeGroup>
  ```typescript Numeric theme={null}
  client.track('page-load-time', 1.42);
  ```

  ```typescript Property theme={null}
  client.track('purchase-event', null, {
    productId: '0x123',
    price: 49.99,
    discount: 0.20
  });
  ```
</CodeGroup>

<Info>
  Create metrics like "Average Purchase Price" from events—see [Metrics Overview](/metrics/overview).
</Info>

### SDK Event Listeners

The SDK emits these events for SDK lifecycle management:

* `SDK_UPDATE`: Emitted when flags are updated
* `SDK_READY`: Emitted when the SDK is ready
* `SDK_READY_FROM_STORE`: Emitted when flags are loaded from storage
* `ERROR`: Emitted when an error occurs during initialization

```typescript theme={null}
import { FsEvent } from '@flagsync/js-sdk';

// Flag updates
client.on(FsEvent.SDK_UPDATE, () => {
  console.log(`Flags updated at ${new Date().toISOString()}`)
});
```

<Note>
  `SDK_UPDATE` does not fire if syncing is disabled.
</Note>

## Error Handling

```typescript theme={null}
import { FsServiceError } from '@flagsync/js-sdk';

// Via events
client.on(FsEvent.ERROR, (error: FsServiceError) => {
  console.error('SDK Error:', error);
});

// Via promises
try {
  await client.waitForReadyCanThrow();
} catch (error) {
  console.error('Initialization Error:', error as FsServiceError);
}
```

<Tip>
  All exposed errors are normalized to `FsServiceError`.
</Tip>

## Configuration

Configure the SDK with the `FsConfig` interface:

```typescript theme={null}
interface FsConfig {
  sdkKey: string;                     // Required: Your SDK key
  core: {
    key: string;                      // Required: User identifier
    attributes?: Record<string, any>; // Optional: Custom user attributes
  };
  bootstrap?: Record<string, any>;    // Optional: Initial flag values
  storage?: {
    type?: 'memory' | 'localstorage'; // Optional: Storage type
    prefix?: string;                  // Optional: Storage key prefix
  };
  sync?: {
    type?: 'ws' | 'sse' | 'poll' | 'off'; // Optional: Sync strategy (default: 'ws')
    pollRateInSec?: number;               // Optional: Polling interval in seconds
  };
  tracking?: {
    impressions?: {
      maxQueueSize: number;           // Required: Max impressions queue size
      pushRateInSec: number;          // Required: Impressions push rate (seconds)
    };
    events?: {
      maxQueueSize: number;           // Required: Max events queue size
      pushRateInSec: number;          // Required: Events push rate (seconds)
    };
  };
  urls?: {
    ws?: string;                      // Optional: WebSocket sync URL
    sse?: string;                     // Optional: SSE sync URL
    flags?: string;                   // Optional: Flag fetch URL
    events?: string;                  // Optional: Events & impressions URL
  };
  logger?: Partial<ILogger>;          // Optional: Custom logger
  logLevel?: LogLevel;                // Optional: Logging level
  metadata?: Record<string, any>;     // Optional: Additional metadata
}
```

### Custom Attributes

Define user attributes for targeting in [Flags: User Segments](/flags/targeting-and-rollouts#user-segments).

```typescript theme={null}
const factory = FlagSyncFactory({
  sdkKey: 'your-sdk-key',
  core: {
    key: 'user-123',
    attributes: {
      plan: 'premium',
      country: 'US',
      userType: 'enterprise'
    }
  }
});
```

<Warning>
  Ensure the `key` in `FsUserContext` is unique and persistent for accurate MAU tracking and consistent flag evaluations. See [User Context Best Practices](/sdks/overview#user-context-best-practices) for details.
</Warning>

### Flag Syncing

Configure flag update strategies with the `sync` object: `ws`, `sse`, `poll`, or `off`.

<Tip>
  By default, flag updates propagate in milliseconds over a WebSocket connection, ensuring the latest values are used in evaluations.
</Tip>

<Steps>
  <Step title="WebSocket (Default)">
    Stream updates over a WebSocket connection—flag updates are reevaluated on the server and pushed to the client in real time:

    ```typescript theme={null}
    const factory = FlagSyncFactory({
      sdkKey: 'your-sdk-key',
      core: { key: 'user-123' },
      sync: {
        type: 'ws' // Default
      }
    });
    ```
  </Step>

  <Step title="SSE">
    Stream updates via server-sent events (SSE):

    ```typescript theme={null}
    const factory = FlagSyncFactory({
      sdkKey: 'your-sdk-key',
      core: { key: 'user-123' },
      sync: {
        type: 'sse'
      }
    });
    ```
  </Step>

  <Step title="Polling">
    Poll the server on an interval:

    ```typescript theme={null}
    const factory = FlagSyncFactory({
      sdkKey: 'your-sdk-key',
      core: { key: 'user-123' },
      sync: {
        type: 'poll',
        pollRateInSec: 60
      }
    });
    ```
  </Step>

  <Step title="Off">
    Disable syncing:

    ```typescript theme={null}
    const factory = FlagSyncFactory({
      sdkKey: 'your-sdk-key',
      core: { key: 'user-123' },
      sync: {
        type: 'off'
      }
    });
    ```
  </Step>
</Steps>

### Bootstrapping

Initialize the SDK with a set of bootstrap flags:

```typescript theme={null}
const factory = FlagSyncFactory({
  sdkKey: 'your-sdk-key',
  core: { key: 'user-123' },
  bootstrap: {
    'my-first-kill-switch': false,
    'another-flag': 'value'
  }
});
```

<Tip>
  Bootstrapped values apply before the SDK is ready—see [Flag Evaluation: Overview](/sdks-flag-evaluation/overview).
</Tip>

### Storage

Choose between memory and LocalStorage storage types:

<Steps>
  <Step title="Memory (Default)">
    ```typescript theme={null}
    const factory = FlagSyncFactory({
      sdkKey: 'your-sdk-key',
      core: { key: 'user-123' },
      storage: {
        type: 'memory'
      }
    });
    ```
  </Step>

  <Step title="LocalStorage">
    ```typescript theme={null}
    const factory = FlagSyncFactory({
      sdkKey: 'your-sdk-key',
      core: { key: 'user-123' },
      storage: {
      type: 'localstorage'
        prefix: 'flagsync' // Optional: Custom prefix for storage keys
      }
    });
    ```

    <Tip>The `SDK_READY_FROM_STORE` event fires when loading from LocalStorage—no network request needed, but data may be stale. </Tip>
  </Step>
</Steps>

## Best Practices

* Wait for SDK readiness before evaluating flags.
* Select a sync strategy (`ws`/`sse`/`poll`/`off`) based on your application’s needs.
* Add user attributes for targeted feature rollouts.
* Consider bootstrapping for faster initial renders.

## Environment Variables

Set the following environment variable:

* `FLAGSYNC_SDK_KEY`: Your client-side FlagSync SDK key (required)
