# React Client SDK

> For AI agents: a documentation index is available at [/llms.txt](/llms.txt). Append `.md` to any page URL for markdown, or send `Accept: text/markdown`.

- **Package:** `@statsig/react-bindings` ([npm](https://www.npmjs.com/package/@statsig/react-bindings))
- **Latest version:** 3.33.3
- **Repository:** [js-client-monorepo](https://github.com/statsig-io/js-client-monorepo)

## Set up the SDK

1. **Install the SDK**

   > **Info:**
   >
   > If you need a starter project, follow the official [React quickstart](https://react.dev/learn/build-a-react-app-from-scratch). Looking for Next.js instead? Go to the [Next.js SDK](https://docs.statsig.com/client/Next) docs.

   ### AI-powered setup

   Setup Statsig in 90 seconds by copying this AI prompt into your IDE:

   ```text
   You are a frontend engineer integrating the Statsig SDK into a React app. Follow these instructions carefully:
   1. Install the required Statsig packages:
        npm install @statsig/react-bindings @statsig/session-replay @statsig/web-analytics

   2. In the main component file (`App.jsx` or `App.tsx`):
      - Import `StatsigProvider` and `useClientAsyncInit` from `@statsig/react-bindings`
      - Import `StatsigAutoCapturePlugin` from `@statsig/web-analytics` and `StatsigSessionReplayPlugin` from `@statsig/session-replay`
      - Initialize the SDK using your client key: 'YOUR-CLIENT-API-KEY'
      - Use `userID` from an existing variable if it's already declared in the file; otherwise, default to `'a-user'`
      - Wrap the existing app content inside `<StatsigProvider>`, using `<div>Loading...</div>` as the `loadingComponent`

   3. DO NOT remove any existing JSX content from the component. Just wrap it.

   4. Here is what the final file structure should look like:

       import { StatsigProvider, useClientAsyncInit } from '@statsig/react-bindings';
       import { StatsigAutoCapturePlugin } from '@statsig/web-analytics';
       import { StatsigSessionReplayPlugin } from '@statsig/session-replay';
       import YourApp from './YourApp';

       function App() {
         const id = typeof userID !== 'undefined' ? userID : 'a-user';
         const { client } = useClientAsyncInit(
           'YOUR-CLIENT-API-KEY',
           { userID: id },
           { plugins: [new StatsigAutoCapturePlugin(), new StatsigSessionReplayPlugin()] }
         );

         return (
           <StatsigProvider client={client} loadingComponent={<div>Loading...</div>}>
             <YourApp />
           </StatsigProvider>
         );
       }

   5. Ask the user to provide their CLIENT-API-KEY and insert it where prompted above.
   ```

   ### Install packages

   #### npm

   ```bash
   npm install @statsig/react-bindings
   ```

   #### yarn

   ```bash
   yarn add @statsig/react-bindings
   ```

   > **Tip:**
   >
   > Add `@statsig/session-replay` and `@statsig/web-analytics` if you plan to enable Session Replay or Auto Capture.
2. **Initialize the SDK**

   Initialize the SDK with a client SDK key from the ["API Keys" tab on the Statsig console](https://console.statsig.com/api_keys). These keys are safe to embed in a client application.

   Along with the key, pass in a [User Object](#statsig-user) with the attributes you'd like to target later on in a gate or experiment.

   ### Wrap your app with `StatsigProvider`

   Provide your client SDK key and initial user when you render the provider.

   ```tsx
   import { StatsigProvider } from '@statsig/react-bindings';

   function App() {
     return (
       <StatsigProvider sdkKey="client-KEY" user={{ userID: '1234', email: 'example@statsig.com' }}>
         <div>Hello world</div>
       </StatsigProvider>
     );
   }
   ```

   ### Typical project structure

   Most projects render a root component inside the provider.

   ```tsx
   // App.tsx
   import RootPage from './RootPage';
   import { StatsigProvider } from '@statsig/react-bindings';

   export default function App() {
     return (
       <StatsigProvider sdkKey="client-KEY" user={{ userID: '1234' }}>
         <RootPage />
       </StatsigProvider>
     );
   }
   ```

   ```tsx
   // RootPage.tsx
   export default function RootPage() {
     return <div>Hello World</div>;
   }
   ```

   > **Info:**
   >
   > Need to balance startup speed with freshness? Review [Initialization Strategies](https://docs.statsig.com/client/concepts/initialize) for bootstrap and async options.

## Use the SDK

Use `useStatsigClient` inside components to retrieve the client when you need to evaluate something.

```tsx
import { useStatsigClient } from '@statsig/react-bindings';

const { client } = useStatsigClient();
```

### Checking a Feature Flag/Gate

Now that your SDK is initialized, check a [**Feature Gate**](https://docs.statsig.com/feature-flags/overview). Feature Gates create logic branches in code that you can roll out to different users from the Statsig Console. Gates are always **CLOSED** or **OFF** (think `return false;`) by default.

```tsx
import {
  useFeatureGate,
  useGateValue,
  useStatsigClient,
} from '@statsig/react-bindings';

const { checkGate } = useStatsigClient();
const gateValue = useGateValue('my_gate');
const gate = useFeatureGate('my_gate');

return (
  <div>
    {checkGate('my_gate') && <p>Passing</p>}
    {gateValue && <p>Passing</p>}
    {gate.value && <p>Passing ({gate.details.reason})</p>}
  </div>
);
```

### Reading a Dynamic Config

Feature Gates are useful for simple on/off switches with optional advanced user targeting. To send a different set of values (strings, numbers, etc.) to clients based on specific user attributes such as country, use **Dynamic Configs**. The API is similar to Feature Gates, but you get a complete JSON object you can configure on the server and fetch typed parameters from it. For example:

```tsx
import { useDynamicConfig, useStatsigClient } from '@statsig/react-bindings';

const config = useDynamicConfig('my_dynamic_config');
const { getDynamicConfig } = useStatsigClient();

return (
  <div>
    <p>Reason: {config.details.reason}</p>
    <p>Value: {config.get('a_value', 'fallback_value')}</p>
    <p>Another Value: {getDynamicConfig('my_dynamic_config').get('a_bool', false)}</p>
  </div>
);
```

### Getting a Layer/Experiment

**Layers/Experiments** let you run A/B/n experiments. Statsig offers two APIs, but recommends [layers](https://docs.statsig.com/experiments/layers-overview) to enable quicker iterations with parameter reuse.

```tsx
import { useExperiment, useStatsigClient } from '@statsig/react-bindings';

const experiment = useExperiment('my_experiment');
const { getExperiment } = useStatsigClient();

return (
  <div>
    <p>Group: {getExperiment('my_experiment').groupName}</p>
    <p>Value: {experiment.get('a_value', 'fallback_value')}</p>
  </div>
);
```

```tsx
import { useLayer, useStatsigClient } from '@statsig/react-bindings';

const layer = useLayer('my_layer');
const { getLayer } = useStatsigClient();

return (
  <div>
    <p>Group: {getLayer('my_layer').groupName}</p>
    <p>Value: {layer.get('a_value', 'fallback_value')}</p>
  </div>
);
```

### Logging an event

After setting up a Feature Gate or Experiment, you may want to track custom events to see how new features or experiment groups affect those events. Call the Log Event API for the event. You can also provide a value and metadata object to log with the event:

```tsx
import { useStatsigClient } from '@statsig/react-bindings';

const { logEvent } = useStatsigClient();

return <button onClick={() => logEvent('my_event')}>Click Me</button>;
```

### Flushing logged events

`flush()` sends queued events immediately. Use `shutdown()` when your app is exiting.

```tsx
import { useStatsigClient } from '@statsig/react-bindings';

const { client } = useStatsigClient();

return (
  <button
    onClick={async () => {
      await client.flush();
    }}
  >
    Flush Events
  </button>
);
```

## Parameter Stores

Parameter Stores hold a set of parameters for your app. You can remap these parameters dynamically from a static value to a Statsig entity (Feature Gates, Experiments, and Layers). This remapping decouples your code from the configuration in Statsig. Refer to [Parameter Stores](https://docs.statsig.com/client/concepts/parameter-stores) for details.

## Manage users

### Updating user properties

Call `updateUserAsync` when a user logs in or when you collect richer attributes.

```tsx
import { useGateValue, useStatsigUser } from '@statsig/react-bindings';

export default function AccountBanner() {
  const gateValue = useGateValue('check_user');
  const { updateUserAsync } = useStatsigUser();

  return (
    <div>
      <div>Gate is {gateValue ? 'passing' : 'failing'}.</div>
      <button onClick={() => updateUserAsync({ userID: '2' })}>Login</button>
    </div>
  );
}
```

## Loading state

To wait for the latest values during initialization, use either the provider or the async hook.

#### StatsigProvider

```tsx
import { StatsigProvider } from '@statsig/react-bindings';

export function App() {
  return (
    <StatsigProvider
      sdkKey="client-KEY"
      user={{ userID: 'a-user' }}
      loadingComponent={<div>Loading...</div>}
    >
      <YourComponent />
    </StatsigProvider>
  );
}
```

#### useClientAsyncInit

```tsx
import { StatsigProvider, useClientAsyncInit } from '@statsig/react-bindings';

export function App() {
  const { client, isLoading } = useClientAsyncInit(
    'client-KEY',
    { userID: 'a-user' },
  );

  if (isLoading) {
    return <div>Loading...</div>;
  }

  return (
    <StatsigProvider client={client}>
      <YourComponent />
    </StatsigProvider>
  );
}
```

## React hooks

> **Warning:**
>
> Hooks that read gates, configs, experiments, or layers log exposures on render. Use `useStatsigClient` to defer checks until you actually change the UI.

### Feature Gate hooks

- Recommended: `useStatsigClient().checkGate` logs when invoked.
- `useGateValue` returns the boolean value and logs immediately.
- `useFeatureGate` returns the full gate object with details.

```tsx
import {
  useFeatureGate,
  useGateValue,
  useStatsigClient,
} from '@statsig/react-bindings';

const { checkGate } = useStatsigClient();
const gateValue = useGateValue('my_gate');
const gate = useFeatureGate('my_gate');

return (
  <div>
    {checkGate('my_gate') && <p>Passing</p>}
    {gateValue && <p>Passing</p>}
    {gate.value && <p>Passing ({gate.details.reason})</p>}
  </div>
);
```

### Dynamic Config hooks

- Recommended: `useStatsigClient().getDynamicConfig` defers exposure until called.
- `useDynamicConfig` logs on render.

```tsx
import { useDynamicConfig, useStatsigClient } from '@statsig/react-bindings';

const config = useDynamicConfig('my_dynamic_config');
const { getDynamicConfig } = useStatsigClient();

return (
  <div>
    <p>Reason: {config.details.reason}</p>
    <p>Value: {config.get('a_value', 'fallback_value')}</p>
    <p>Another Value: {getDynamicConfig('my_dynamic_config').get('a_bool', false)}</p>
  </div>
);
```

### Experiment hooks

- Recommended: `useStatsigClient().getExperiment` to control exposures.
- `useExperiment` logs on render.

```tsx
import { useExperiment, useStatsigClient } from '@statsig/react-bindings';

const experiment = useExperiment('my_experiment');
const { getExperiment } = useStatsigClient();

return (
  <div>
    <p>Group: {getExperiment('my_experiment').groupName}</p>
    <p>Value: {experiment.get('a_value', 'fallback_value')}</p>
  </div>
);
```

### Layer hooks

Layers only log exposures when you call `.get()`.

```tsx
import { useLayer, useStatsigClient } from '@statsig/react-bindings';

const layer = useLayer('my_layer');
const { getLayer } = useStatsigClient();

return (
  <div>
    <p>Group: {getLayer('my_layer').groupName}</p>
    <p>Value: {layer.get('a_value', 'fallback_value')}</p>
  </div>
);
```

### Parameter Store hooks

```tsx
import { useParameterStore } from '@statsig/react-bindings';

function MyComponent() {
  const store = useParameterStore('my_parameter_store');
  const title = store.get('page_title', 'Default Title');
  const maxItems = store.get('max_items', 10);
  const isEnabled = store.get('feature_enabled', false);

  const storeNoExposure = useParameterStore('my_parameter_store', {
    disableExposureLog: true,
  });

  return <div>{title}</div>;
}
```

### Log events from hooks

```tsx
import { useStatsigClient } from '@statsig/react-bindings';

const { logEvent } = useStatsigClient();

return <button onClick={() => logEvent('my_event')}>Click Me</button>;
```

### StatsigUser hook

```tsx
import { useStatsigUser } from '@statsig/react-bindings';

const { user, updateUserSync } = useStatsigUser();

return (
  <div>
    <p>Current User: {user.userID}</p>
    <button onClick={() => updateUserSync({ userID: 'some-other-user' })}>
      Update User
    </button>
  </div>
);
```

### Direct access to the client

```tsx
import { useStatsigClient } from '@statsig/react-bindings';

const { client } = useStatsigClient();
console.log('stableID', client.getContext().stableID);
```

### Client initialization hooks

- `useClientAsyncInit`: fetches the latest values before rendering.
- `useClientBootstrapInit`: bootstrap from server-provided values.

> **Info:**
>
> You can also initialize your own client instance manually. Refer to [Initialization Strategies](https://docs.statsig.com/client/concepts/initialize) for alternatives.

## Statsig options

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| loggingEnabled | LoggingEnabledOption | No | browser-only | Controls logging behavior.- `browser-only` (default): log events from browser environments. - `disabled`: never send events. - `always`: log in every environment, including non-browser contexts. |

Use `loggingEnabled: 'disabled'` instead.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| — | — | No | — | — |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| disableStableID | boolean | No | false | Skip generating a device-level Stable ID. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| disableEvaluationMemoization | boolean | No | false | Recompute every evaluation instead of using the memoized result. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| initialSessionID | string | No | — | Override the generated session ID. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| enableCookies | boolean | No | false | Persist Stable ID in cookies for cross-domain tracking. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| disableStorage | boolean | No | — | Prevent any local storage writes (disables caching). |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| networkConfig | NetworkConfig | No | — | Override network endpoints per request type. |

#### Network Config Options

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| api | string | No | https://api.statsig.com | Base URL for all requests. The SDK appends endpoint paths like `/initialize` and `/rgstr`; append `/v1` when your proxy expects it. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| initializeUrl | string | No | https://featureassets.org/v1/initialize | Endpoint for initialization requests only. Takes precedence over `api` for `/initialize`. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| initializeFallbackUrls | string[] | No | — | Fallback endpoints for initialization requests only. This doesn't create a generic fallback for `api`. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| logEventUrl | string | No | https://prodregistryv2.org/v1/rgstr | Endpoint for event uploads. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| logEventFallbackUrls | string[] | No | — | Fallback endpoints for event uploads only. This doesn't create a generic fallback for `api`. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| networkTimeoutMs | number | No | 10000 | Request timeout in milliseconds. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| preventAllNetworkTraffic | boolean | No | — | Disable all outbound requests; combine with `loggingEnabled: 'disabled'` to silence log warnings. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| networkOverrideFunc | function | No | — | Provide custom transport (e.g., Axios). |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| environment | StatsigEnvironment | No | — | Set environment-wide defaults (for example `{ tier: 'staging' }`). |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| logLevel | LogLevel | No | Warn | Console verbosity. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| loggingBufferMaxSize | number | No | 50 | Max events per log batch. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| loggingIntervalMs | number | No | 10_000 | Interval between automatic flushes. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| overrideAdapter | OverrideAdapter | No | — | Modify evaluations before returning them. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| includeCurrentPageUrlWithEvents | boolean | No | true | Attach the current page URL to logged events. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| disableStatsigEncoding | boolean | No | false | Send requests without Statsig-specific encoding. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| logEventCompressionMode | LogEventCompressionMode | No | Enabled | Control compression for batched events. |

Use `logEventCompressionMode` instead.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| — | — | No | — | — |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| dataAdapter | EvaluationsDataAdapter | No | — | Provide a custom data adapter to control caching/fetching. |

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| customUserCacheKeyFunc | CustomCacheKeyGenerator | No | — | Override cache key generation for stored evaluations. |

## Testing

Mock Statsig hooks in Jest to isolate component logic.

```tsx
import { StatsigProvider, useFeatureGate, useExperiment } from '@statsig/react-bindings';

function Content() {
  const gate = useFeatureGate('a_gate');
  const experiment = useExperiment('an_experiment');

  return (
    <div>
      <div data-testid="gate_test">a_gate: {gate.value ? 'Pass' : 'Fail'}</div>
      <div data-testid="exp_test">
        an_experiment: {experiment.get('my_param', 'fallback')}
      </div>
    </div>
  );
}

function App() {
  return (
    <StatsigProvider
      sdkKey={YOUR_CLIENT_KEY}
      user={{ userID: 'a-user' }}
      options={{
        networkConfig: {
          // Optional – disable network requests in tests
          preventAllNetworkTraffic:
            typeof process !== 'undefined' && process.env['NODE_ENV'] === 'test',
        },
      }}
    >
      <Content />
    </StatsigProvider>
  );
}
```

```tsx
import { render, screen } from '@testing-library/react';
import * as ReactBindings from '@statsig/react-bindings';

jest.mock('@statsig/react-bindings', () => ({
  ...jest.requireActual('@statsig/react-bindings'),
  useFeatureGate: () => ({ value: true }),
  useExperiment: () => ({ get: () => 'my_value' }),
}));

test('renders gate pass', async () => {
  render(<App />);
  const elem = await screen.findByTestId('gate_test');
  expect(elem.textContent).toContain('Pass');
});

test('renders experiment value', async () => {
  render(<App />);
  const elem = await screen.findByTestId('exp_test');
  expect(elem.textContent).toContain('my_value');
});
```

## Lifecycle & advanced usage

## Shutting Statsig down

The SDK keeps event logs in the client cache and flushes them periodically to save data and battery usage. Because the SDK flushes only periodically, it may not have flushed some events when your app shuts down.

To ensure the SDK flushes or saves all logged events locally, shut down Statsig when your app is closing:

```tsx
import { useEffect } from 'react';
import { useStatsigClient } from '@statsig/react-bindings';

const { client } = useStatsigClient();

useEffect(() => {
  return () => {
    void client.shutdown();
  };
}, [client]);
```

## Session Replay

Install `@statsig/session-replay` and register the plugin to record user sessions.

```tsx
import { StatsigProvider, useClientAsyncInit } from '@statsig/react-bindings';
import { StatsigSessionReplayPlugin } from '@statsig/session-replay';

function App() {
  const { client } = useClientAsyncInit(
   'client-KEY',
    { userID: 'a-user' },
    { plugins: [new StatsigSessionReplayPlugin()] },
  );

  return (
    <StatsigProvider client={client} loadingComponent={<div>Loading...</div>}>
      <div>Hello World</div>
    </StatsigProvider>
  );
}
```

## Web Analytics / Auto Capture

By including the [`@statsig/web-analytics`](https://www.npmjs.com/package/@statsig/web-analytics) package in your project, you can automatically capture common web events like clicks and page views.

For more information on filtering events, enabling console log capture, and other configuration options in web analytics, refer to the [Web Analytics Configuration](https://docs.statsig.com/webanalytics/overview#event-filtering-and-console-configuration) documentation.

```tsx
import { StatsigProvider, useClientAsyncInit } from '@statsig/react-bindings';
import { StatsigAutoCapturePlugin } from '@statsig/web-analytics';

function App() {
  const { client } = useClientAsyncInit(
   'client-KEY',
    { userID: 'a-user' },
    { plugins: [new StatsigAutoCapturePlugin()] },
  );

  return (
    <StatsigProvider client={client} loadingComponent={<div>Loading...</div>}>
      <div>Hello World</div>
    </StatsigProvider>
  );
}
```

## Using persistent evaluations

To keep experiment variants stable across rerenders or user transitions, use persistent storage. The React integration mirrors the [JavaScript workflow](https://docs.statsig.com/client/javascript-sdk#using-persistent-evaluations) and you can adapt the [Next.js sample](https://github.com/statsig-io/js-client-monorepo/tree/main/samples/next-js/src/app/persisted-user-storage-example) to your setup.

Read more in [Client Persistent Assignment](https://docs.statsig.com/client/concepts/persistent_assignment).

## Additional resources

- [Initialization Concepts](https://docs.statsig.com/client/concepts/initialize)
- [JavaScript Client SDK](https://docs.statsig.com/client/javascript-sdk)
- [Persistent Assignment](https://docs.statsig.com/client/concepts/persistent_assignment)
