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

# React Web SDK 3.x to 4.0 migration guide

<View title="Developer" />

<View title="Federal docs" />

<View title="EU docs" />

This topic explains the changes in the React Web SDK 4.0 release and how to adapt code that uses a 3.x version of the [React Web SDK](/docs/sdk/client-side/react/react-web) to use version 4 or later.

After you complete the migration, your application will be running the React Web SDK version 4.0.

To learn more, read the [React Web SDK reference](/docs/sdk/client-side/react/react-web) and [React Server Component support](/docs/sdk/client-side/react/react-web#react-server-component-support).

Before you migrate to version 4, update to the latest 3.x version. Some of the changes that are mandatory in 4 were originally added in a 3.x version and made optional.

<Note>
  **Review the JavaScript SDK 3.x to 4.0 migration guide too**

  The React Web SDK is based on `@launchdarkly/js-client-sdk` which is our JavaScript SDK at version 4.0.0 and higher.

  To learn about the differences between that version and the older versions of the JavaScript SDK, read the [JavaScript SDK 3.x to 4.0 migration guide](/docs/sdk/client-side/javascript/migration-3-to-4). Those changes are also relevant to the React SDK.
</Note>

## Prerequisites

To complete this migration, you must have the following prerequisites:

* A LaunchDarkly account.
* React version 18.0.0 or higher. We recommend using React 19 if possible.
* React Web SDK version 3.x installed.

React Server Components are stable in React 19, but are only stable in some frameworks. You must be on React 19 to leverage the server submodule. An earlier version of React may work if you are only using the `@launchdarkly/react-sdk` package, but it is not officially supported.

## Understanding what changed in version 4.0

<Note>
  **Introducing support for React Server Components**

  This update includes support for React Server Components. That functionality is included in the React Web SDK. To learn more, read [React Server Component support](/docs/sdk/client-side/react/react-web#react-server-component-support).
</Note>

There are several significant changes to version 4.0 of the React Web SDK.

* [Named contexts](#named-contexts)
* [`useInitializationStatus` replaces `useLDClientError`](#useinitializationstatus-replaces-useldclienterror)
* [`useInitializationStatus` replaces `asyncWithLDProvider`](#useinitializationstatus-replaces-asyncwithldprovider)
* [Removed higher-order components: `withLDProvider` and `asyncWithLDProvider`](#removed-higher-order-components-withldprovider-and-asyncwithldprovider)
* [Changed deferred initialization behavior](#changed-deferred-initialization-behavior)
* [Bootstrap is now a top-level option](#bootstrap-is-now-a-top-level-option)
* [Deprecations](#deprecations)
* [Removed APIs](#removed-apis)

### Named contexts

Version 4.0 supports custom React contexts with the `reactContext` option on `createLDReactProvider` and through an optional argument on every hook.

Here's an example:

<CodeGroup>
  ```js title="React Web SDK v3.x" lines wrap theme={null}
  import { withLDProvider, withLDConsumer } from 'launchdarkly-react-client-sdk';
  const MyContext = React.createContext(null);

  export default withLDProvider({
    clientSideID: 'your-id',
    reactContext: MyContext,
  })(App);

  const MyComponent = withLDConsumer({ reactContext: MyContext })(({ flags, ldClient }) => (
    <div>{flags.myFlag ? 'on' : 'off'}</div>
  ));
  ```

  ```js title="React Web SDK v4.0" lines wrap theme={null}
  import { initLDReactContext, createLDReactProvider, useBoolVariation, useLDClient } from '@launchdarkly/react-sdk';

  const MyContext = initLDReactContext();
  const LDProvider = createLDReactProvider('your-id', { kind: 'user', key: 'user-key' }, {
    reactContext: MyContext,
  });

  function MyComponent() {
    const myFlag   = useBoolVariation('my-flag', false, MyContext);
    const ldClient = useLDClient(MyContext);
    return <div>{myFlag ? 'on' : 'off'}</div>;
  }
  ```
</CodeGroup>

### `useInitializationStatus` replaces `useLDClientError`

The old `useLDClientError` only returned an error when the client failed to initialize. The new `useInitializationStatus()` provides both the full initialization state and the error.

<CodeGroup>
  ```js title="React Web SDK v3.x" lines wrap theme={null}
  import { useLDClientError } from 'launchdarkly-react-client-sdk';
  const error = useLDClientError(); // Error | undefined
  ```

  ```js title="React Web SDK v4.0" lines wrap theme={null}
  import { useInitializationStatus } from '@launchdarkly/react-sdk';
  const { status, error } = useInitializationStatus();
  // status: 'initializing' | 'complete' | 'timeout' | 'failed'
  // error: Error | undefined (only present when status === 'failed')
  ```
</CodeGroup>

`useInitializationStatus` tells you the full state of initialization, not only if there was an error. Use `status` to gate rendering and `error` for error reporting.

### `useInitializationStatus` replaces `asyncWithLDProvider`

The old function blocked rendering until the client initialized. In version 4, you can use `useInitializationStatus` to conditionally render loading or error states.

<CodeGroup>
  ```js title="React Web SDK v3.x" lines wrap theme={null}
  // `asyncWithLDProvider` blocked until flags were ready
  const LDProvider = await asyncWithLDProvider({ clientSideID: 'your-client-side-id' });
  root.render(<LDProvider><App /></LDProvider>);
  ```

  ```js title="React Web SDK v4.0" lines wrap theme={null}
  // Provider renders immediately. Gate your UI with `useInitializationStatus`
  const LDProvider = createLDReactProvider('your-client-side-id', { kind: 'user', key: 'example-user-key' });

  function App() {
    const { status, error } = useInitializationStatus();
    if (status === 'initializing') return <LoadingSpinner />;
    if (status === 'failed') return <ErrorMessage error={error} />;
    return <YourApp />;
  }

  root.render(<LDProvider><App /></LDProvider>);
  ```
</CodeGroup>

### Removed higher-order components: `withLDProvider` and `asyncWithLDProvider`

We've removed our higher-order components as the React ecosystem now heavily favors the hooks pattern.
We strongly recommend migrating to use our `createLDReactProvider` for client instantiation.

However, if you prefer to continue using HOCs, you can create a HOC using our alternative
[`createClient`](https://launchdarkly.github.io/js-core/packages/sdk/react/docs/functions/client.createClient.html).

<CodeGroup>
  ```js title="React Web SDK v4.0" lines wrap theme={null}
  import { createClient } from '@launchdarkly/react-sdk';

  const client = createClient('your-client-side-id', { kind: 'user', key: 'example-user-key' });

  export default (WrappedComponent) => {
    return (props) => {
      return <WrappedComponent {...props} ldClient={client} />
    }
  }
  ```
</CodeGroup>

Remember, `createClient` does not automatically call the `start()` method that kicks off the SDK
client initialization.

### Changed deferred initialization behavior

`deferInitialization` has changed option location, its default value, how it behaves when set to `true`, and how it starts manually.

<table>
  <thead>
    <tr>
      <th>Initialization subject</th>
      <th>React Web SDK v3.x</th>
      <th>React Web SDK v4.0</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>Option location</td>

      <td>
        <code>
          {'withLDProvider({ deferInitialization })'}
        </code>
      </td>

      <td>
        <code>
          {'createLDReactProvider(id, ctx, { deferInitialization })'}
        </code>
      </td>
    </tr>

    <tr>
      <td>Default</td>
      <td><code>false</code> (auto-initialize on mount)</td>
      <td><code>false</code> (auto-start before provider mounts)</td>
    </tr>

    <tr>
      <td>When <code>true</code></td>
      <td>Provider does not call <code>initialize()</code>; user calls <code>ldClient.identify(context)</code> to start.</td>
      <td>Factory does not call <code>start()</code>; user must call <code>client.start()</code> manually.</td>
    </tr>

    <tr>
      <td>How to start manually</td>
      <td><code>ldClient.identify(context)</code>. Identify doubled as init in the old SDK</td>
      <td><code>client.start()</code>. An explicit start is required. <code>identify()</code> switches context only after <code>start()</code></td>
    </tr>
  </tbody>
</table>

If you use the low-level API, `createLDReactProviderWithClient` always behaves as if `deferInitialization: true`. It never calls `start()` automatically.

### Bootstrap is now a top-level option

In v3.x, bootstrap data was nested inside `options`. In v4.0, `bootstrap` is a top-level option on `createLDReactProvider`.

The `bootstrap` data format is unchanged. You can pass either a plain key-value object, such as `{ 'example-flag-key': true }`, or the output of `allFlagsState().toJSON()`, which includes `$flagsState` and `$valid` metadata.

<CodeGroup>
  ```js title="React Web SDK v3.x" lines wrap theme={null}
  // Higher-order component
  withLDProvider({
    clientSideID: 'your-client-side-id',
    options: { bootstrap: myData },
  })(App);

  // Async higher-order component
  const LDProvider = await asyncWithLDProvider({
    clientSideID: 'your-client-side-id',
    options: { bootstrap: myData },
  });
  ```

  ```js title="React Web SDK v4.0" lines wrap theme={null}
  import { createLDReactProvider } from '@launchdarkly/react-sdk';

  const LDProvider = createLDReactProvider(
    'your-client-side-id',
    { kind: 'user', key: 'user-key' },
    {
      bootstrap: myData,
    },
  );
  ```
</CodeGroup>

You can also pass bootstrap data through [`startOptions.bootstrap`](https://launchdarkly.github.io/js-core/packages/sdk/react/docs/interfaces/client.LDStartOptions.html). The top-level `bootstrap` option is merged into `startOptions.bootstrap` when the client starts. If you provide both, the top-level value takes precedence.

<CodeGroup>
  ```js title="React Web SDK v4.0" lines wrap theme={null}
  import { createLDReactProvider } from '@launchdarkly/react-sdk';

  const LDProvider = createLDReactProvider(
    'your-client-side-id',
    { kind: 'user', key: 'user-key' },
    {
      startOptions: {
        bootstrap: myData,
      },
    },
  );
  ```
</CodeGroup>

### Deprecations

`useFlags` is available, but deprecated. We strongly recommend using typed variation hooks for flag evaluations instead.

If you must, you can call `allFlags` from the LDClient. Here's how:

<CodeGroup>
  ```js title="React Web SDK v4.0" lines wrap theme={null}
  import { useLDClient } from '@launchdarkly/react-sdk';

  const ldClient = useLDClient();
  const flags = ldClient.allFlags();
  ```
</CodeGroup>

### Removed APIs

The following APIs have been removed:

* `useLDClientError()`, replaced with `useInitializationStatus().error`.
* `withLDProvider()`, replaced with `createLDReactProvider()`.
* `asyncWithLDProvider()`, replaced with `createLDReactProvider()`.
* `withLDConsumer()`, replaced with `useLDClient()`, and typed variation hooks such as `useBoolVariation()`, `useStringVariation()`, `useNumberVariation()`, `useJsonVariation()`.
* `LDProvider` component, replaced with `createLDReactProvider()`.
* `reactSdkContextFactory()`, replaced with `initLDReactContext()`. To learn more, read [Named contexts](#named-contexts), above.
* `camelCaseKeys()` utility function. Camel-case key conversion is no longer exposed as a standalone helper. The typed variation hooks always use the original flag key. The `useFlags` hook still applies camel-casing internally and can be opted out of with `ldOptions.useCamelCaseFlagKeys: false`.

## Updating your dependencies

`@launchdarkly/js-client-sdk` is bundled as a transitive dependency of `@launchdarkly/react-sdk`. You do not need to install or update it manually.
