
{% callout type="info" %}
Source code: [statsig-io/js-client-monorepo](https://github.com/statsig-io/js-client-monorepo)
{% /callout %}

## Set Up the SDK

{% steps %}
{% step title="Install the SDK" %}
{% callout type="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](/client/Next) docs.
{% /callout %}

### AI-powered Setup

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

```text expandable
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

{% tabs %}
{% tab name="npm" %}
```bash
npm install @statsig/react-bindings
```
{% /tab %}

{% tab name="yarn" %}
```bash
yarn add @statsig/react-bindings
```
{% /tab %}
{% /tabs %}

{% callout type="tip" %}
Add `@statsig/session-replay` and `@statsig/web-analytics` if you plan to enable Session Replay or Auto Capture.
{% /callout %}
{% /step %}

{% step title="Initialize the SDK" %}
Next, 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>;
}
```

{% callout type="info" %}
Need to balance startup speed with freshness? Review [Initialization Strategies](/client/concepts/initialize) for bootstrap and async options.
{% /callout %}
{% /step %}
{% /steps %}

## 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, let's check a [**Feature Gate**](/feature-flags/overview). Feature Gates can be used to create logic branches in code that can be rolled 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. Two APIs are available, but Statsig recommends [layers](/experiments/layers-overview) for 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 be logged 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. These parameters can be remapped dynamically from a static value to a Statsig entity (Feature Gates, Experiments, and Layers), so you can decouple your code from the configuration in Statsig. Refer to [Parameter Stores](/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.

{% tabs %}
{% tab name="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>
  );
}
```
{% /tab %}

{% tab name="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>
  );
}
```
{% /tab %}
{% /tabs %}

## React Hooks

{% callout type="warning" %}
Hooks that read gates, configs, experiments, or layers will log exposures on render. Use `useStatsigClient` to defer checks until you actually change the UI.
{% /callout %}

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

{% callout type="info" %}
You can also initialize your own client instance manually. Refer to [Initialization Strategies](/client/concepts/initialize) for alternatives.
{% /callout %}

## Statsig Options

{% parameter name="loggingEnabled" type="LoggingEnabledOption" default="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.
{% /parameter %}

{% parameter name="disableLogging" type="boolean" post="["deprecated"]" %}
Use `loggingEnabled: 'disabled'` instead.
{% /parameter %}

{% parameter name="disableStableID" type="boolean" default="false" %}
Skip generating a device-level Stable ID.
{% /parameter %}

{% parameter name="disableEvaluationMemoization" type="boolean" default="false" %}
Recompute every evaluation instead of using the memoized result.
{% /parameter %}

{% parameter name="initialSessionID" type="string" %}
Override the generated session ID.
{% /parameter %}

{% parameter name="enableCookies" type="boolean" default="false" %}
Persist Stable ID in cookies for cross-domain tracking.
{% /parameter %}

{% parameter name="disableStorage" type="boolean" %}
Prevent any local storage writes (disables caching).
{% /parameter %}

{% parameter name="networkConfig" type="NetworkConfig" %}
Override network endpoints per request type.
{% /parameter %}

{% accordion title="Network Config Options" %}
{% parameter name="api" type="string" default="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.
{% /parameter %}

{% parameter name="initializeUrl" type="string" default="https://featureassets.org/v1/initialize" %}
Endpoint for initialization requests only. Takes precedence over `api` for `/initialize`.
{% /parameter %}

{% parameter name="initializeFallbackUrls" type="string[]" %}
Fallback endpoints for initialization requests only. This doesn't create a generic fallback for `api`.
{% /parameter %}

{% parameter name="logEventUrl" type="string" default="https://prodregistryv2.org/v1/rgstr" %}
Endpoint for event uploads.
{% /parameter %}

{% parameter name="logEventFallbackUrls" type="string[]" %}
Fallback endpoints for event uploads only. This doesn't create a generic fallback for `api`.
{% /parameter %}

{% parameter name="networkTimeoutMs" type="number" default="10000" %}
Request timeout in milliseconds.
{% /parameter %}

{% parameter name="preventAllNetworkTraffic" type="boolean" %}
Disable all outbound requests; combine with `loggingEnabled: 'disabled'` to silence log warnings.
{% /parameter %}

{% parameter name="networkOverrideFunc" type="function" %}
Provide custom transport (e.g., Axios).
{% /parameter %}
{% /accordion %}

{% parameter name="environment" type="StatsigEnvironment" %}
Set environment-wide defaults (for example `{ tier: 'staging' }`).
{% /parameter %}

{% parameter name="logLevel" type="LogLevel" default="Warn" %}
Console verbosity.
{% /parameter %}

{% parameter name="loggingBufferMaxSize" type="number" default="50" %}
Max events per log batch.
{% /parameter %}

{% parameter name="loggingIntervalMs" type="number" default="10_000" %}
Interval between automatic flushes.
{% /parameter %}

{% parameter name="overrideAdapter" type="OverrideAdapter" %}
Modify evaluations before returning them.
{% /parameter %}

{% parameter name="includeCurrentPageUrlWithEvents" type="boolean" default="true" %}
Attach the current page URL to logged events.
{% /parameter %}

{% parameter name="disableStatsigEncoding" type="boolean" default="false" %}
Send requests without Statsig-specific encoding.
{% /parameter %}

{% parameter name="logEventCompressionMode" type="LogEventCompressionMode" default="Enabled" %}
Control compression for batched events.
{% /parameter %}

{% parameter name="disableCompression" type="boolean" post="["deprecated"]" %}
Use `logEventCompressionMode` instead.
{% /parameter %}

{% parameter name="dataAdapter" type="EvaluationsDataAdapter" %}
Provide a custom data adapter to control caching/fetching.
{% /parameter %}

{% parameter name="customUserCacheKeyFunc" type="CustomCacheKeyGenerator" %}
Override cache key generation for stored evaluations.
{% /parameter %}

## 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 of this, the SDK may not have flushed some events when your app shuts down.

To ensure all logged events are flushed or saved 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 available in web analytics, refer to the [Web Analytics Configuration](/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](/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](/client/concepts/persistent_assignment).

## Additional Resources

* [Initialization Concepts](/client/concepts/initialize)
* [JavaScript Client SDK](/client/javascript-sdk)
* [Persistent Assignment](/client/concepts/persistent_assignment)
