> ## 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.

# React SDK

> Integrate FlagSync feature flags into React applications using hooks and providers.

## Overview

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

> Requires React 16.3+ due to its dependency on the React Context API.

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

  <Card title="GitHub" icon="github" href="https://github.com/flagsync/react-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/react-sdk
  npm install -D @flagsync/cli
  ```

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

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

## Quickstart

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

```jsx theme={null}
import { useFlag, FlagSyncProvider } from '@flagsync/react-sdk';

function MyApp() {
  return (
    <FlagSyncProvider config={{
      sdkKey: 'your-sdk-key',
      core: {
        key: 'user-123'
      }
    }}>
      <MyComponent />
    </FlagSyncProvider>
  );
}

function MyComponent() {
  const { value, isReady, isReadyFromStore } = useFlag('my-first-kill-switch', false);

  if (!isReady) {
    return <div>Loading...</div>;
  }

  return <div>Feature is {value ? 'enabled' : 'disabled'}</div>;
}
```

## 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

Wrap your application with `FlagSyncProvider` and provide the required configuration:

```tsx theme={null}
import { FlagSyncProvider } from '@flagsync/react-sdk';

function App() {
  return (
    <FlagSyncProvider
      config={{
        sdkKey: 'your-sdk-key',
        core: {
          key: 'user-123'
        }
        // other configuration options...
      }}
    >
      {/* Your app components */}
    </FlagSyncProvider>
  );
}
```

<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

Prevent flickering by ensuring the SDK is ready before your app renders.

Use the `waitForReady` prop in `FlagSyncProvider` to delay rendering until the SDK is initialized, or check readiness manually with hooks or render prop components:

<Steps>
  <Step title="Using waitForReady">
    Add `waitForReady` to `FlagSyncProvider` to delay rendering until the SDK is ready, avoiding flicker during initialization.

    ```tsx theme={null}
    <FlagSyncProvider waitForReady config={config}>
      <MyApp />
    </FlagSyncProvider>
    ```
  </Step>

  <Step title="Using hooks or render prop components">
    Check isReady with hooks like `useFlag`, `useFlags`, or `useFlagSyncClient` to control rendering

    <CodeGroup>
      ```typescript useFlag theme={null}
      const { isReady, isReadyFromStore } = useFlag('my-first-kill-switch', false);
      ```

      ```typescript useFlags theme={null}
      const { isReady, isReadyFromStore } = useFlags()
      ```

      ```typescript useFlagSyncClient theme={null}
      const { isReady, isReadyFromStore } = useFlagSyncClient();
      ```
    </CodeGroup>

    <Info>
      Identical properties are provided in `<FlagSyncFlag />`, and `<FlagSyncFlags />`.
    </Info>
  </Step>
</Steps>

#### SDK Not Ready

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

```typescript theme={null}
// value: false (defaultValue) isReady: false
const { value, isReady } = useFlag('flag-one', false); 

// value: "control", isReady: false
const { value, isReady } = useFlag('flag-two');      
```

#### SDK Ready

Once ready, hooks return the server-evaluated value:

```typescript theme={null}
// value: Server-evaluated value, isReady: true
const { value, isReady } = useFlag('flag-one'); 
```

<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 user context. Applies targeting rules, rollouts, and defaults values.

<CodeGroup>
  ```typescript useFlag theme={null}
  // Get a single flag value
  const { value } = useFlag('my-first-kill-switch', defaultValue);
  ```

  ```typescript useFlags theme={null}
  // Get multiple flag values
  const { flags } = useFlags({ 'flag-1': false, 'flag-2': 'dark-mode' });
  ```
</CodeGroup>

#### FlagSync CLI

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

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

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

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

<Tip>
  An [Impression](/impressions/generating) is automatically logged either of the flag hooks are called.
</Tip>

### Track Events

Submit user actions with the `useTrack` hook, which supports two usage patterns:

<Steps>
  <Step title="With a pre-filled event key">
    Bind the hook to a specific event key for repeated tracking:

    ```tsx theme={null}
    const track = useTrack('page-visit'); // Bound to eventKey

    useEffect(() => track())
    ```
  </Step>

  <Step title="With a dynamic event key">
    Use a generic track function to specify the event key at call time:

    ```tsx theme={null}
    const track = useTrack();

    <Button onClick={() => {
      track('purchase-event', null, { productId: 'prod-123' })
    }}>
      Buy Now
    </Button>
    ```
  </Step>
</Steps>

<Tip>
  * See [JavaScript SDK: Track Events](/sdks-client-side/javascript#track-events) for more on `track()`.
  * See [Events: Tracking](/events/tracking) to learn about numeric and property events.
</Tip>

### 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

Access these events using the `useFlagSyncClient` hook:

```tsx theme={null}
import {useFlagSyncClient} from '@flagsync/react-sdk'

const client = useFlagSyncClient();

useEffect(() => {
  const onUpdate = () => {
    console.log('Flags updated:', client.lastUpdated);
  };

  client.on('SDK_UPDATE', onUpdate);
  return () => client.off('SDK_UPDATE', onUpdate);
}, [client]);
```

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

## 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).

```tsx theme={null}
<FlagSyncProvider
    config={{
      sdkKey: 'your-sdk-key',
      core: {
        key: 'user-123',
        attributes: {
          plan: 'premium',
          country: 'US',
          userType: 'enterprise'
        }
      }
    }}
>
  {/* Your app */}
</FlagSyncProvider>
```

<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>

<Info>
  * Components using flag hooks re-render automatically—no event listeners needed.
  * To disable this behavior, set `sync` to `off`, requiring a page refresh to fetch updated flags.
</Info>

<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 config: FsConfig = {
      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 config: FsConfig = {
      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 config: FsConfig = {
      sdkKey: 'your-sdk-key',
      core: { key: 'user-123' },
      sync: {
        type: 'poll',
        pollRateInSec: 60
      }
    };
    ```
  </Step>

  <Step title="Off">
    Disable syncing:

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

### Bootstrapping

Initialize the SDK with a set of bootstrap flags:

```tsx theme={null}
<FlagSyncProvider
    config={{
      sdkKey: 'your-sdk-key',
      core: { key: 'user-123' },
      bootstrap: {
        'feature-1': true,
        'feature-2': false
      }
    }}
>
  {/* Your app */}
</FlagSyncProvider>
```

<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 config: FsConfig = {
      sdkKey: 'your-sdk-key',
      core: { key: 'user-123' },
      storage: {
        type: 'memory'
      }
    };
    ```
  </Step>

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

    <Tip>
      Use the `useFlagSyncClient` to get the `client.isReadyFromStore` property.
    </Tip>
  </Step>
</Steps>

## Best Practices

* Use `isReady` checks to handle loading states. See [Wait for Readiness](#wait-for-readiness).
* Select a sync strategy (`ws`/`sse`/`poll`/`off`) based on your application’s needs.
* Use `useTrack` for consistent event tracking in components.
* 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)
