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

# Basic setup

> Migrate provider setup, context access, and the event model from the React SDK to the JavaScript SDK.

This page covers the foundational changes: how you create and provide the SDK, how you read state, and the shift from an event-driven model to an imperative one. Read the migration overview first if you haven't.

## Provider and client

In the React SDK a single `DynamicContextProvider` created the SDK, held configuration, and exposed everything through `useDynamicContext()`. In the JavaScript SDK you create the client yourself with `createDynamicClient()` and, in React, pass it to a thin `DynamicProvider`. State is read through focused hooks instead of one context object.

Create the client once in a module that loads at startup and register your chain extensions. The client initializes itself automatically.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    // dynamicClient.ts
    import { createDynamicClient } from '@dynamic-labs-sdk/client';
    import { addEvmExtension } from '@dynamic-labs-sdk/evm';

    export const dynamicClient = createDynamicClient({
      environmentId: 'YOUR_ENVIRONMENT_ID',
      metadata: {
        name: 'My App',
        universalLink: window.location.origin,
      },
    });

    addEvmExtension();
    ```
  </Tab>

  <Tab title="React">
    ```tsx theme={"system"}
    // main.tsx
    import { StrictMode } from 'react';
    import { createRoot } from 'react-dom/client';
    import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
    import { DynamicProvider } from '@dynamic-labs-sdk/react-hooks';

    import { App } from './App';
    import { dynamicClient } from './dynamicClient';

    const queryClient = new QueryClient();

    createRoot(document.getElementById('root')!).render(
      <StrictMode>
        <QueryClientProvider client={queryClient}>
          <DynamicProvider client={dynamicClient}>
            <App />
          </DynamicProvider>
        </QueryClientProvider>
      </StrictMode>,
    );
    ```
  </Tab>
</Tabs>

<Note>
  In React, `@dynamic-labs-sdk/react-hooks` is built on TanStack Query, so a `QueryClientProvider` must wrap `DynamicProvider`. See the [React Quickstart](/docs/javascript/reference/react-quickstart) for the full setup.
</Note>

## Reading state

`useDynamicContext()` no longer exists. Instead, import the specific hook (React) or read the equivalent property/function on the client instance (vanilla) for the state you need.

| React SDK                              | JavaScript SDK                                                                        |
| -------------------------------------- | ------------------------------------------------------------------------------------- |
| `useDynamicContext().user`             | `useUser()` / `dynamicClient.user`                                                    |
| `useDynamicContext()`'s tracked wallet | `useGetWalletAccounts()` / `getWalletAccounts()` — pass the specific account you mean |
| `useDynamicContext().network`          | `getActiveNetworkData()`                                                              |
| `useReinitialize()`                    | `initializeClient()`                                                                  |
| `useDynamicEvents()`                   | `useOnEvent()` / `useOnceEvent()` (or `onEvent()` / `offEvent()`)                     |

## Waiting for initialization

The React SDK gated your app behind its own loading state. Now you decide when the client is ready — either await a helper (vanilla) or render off the init status (React).

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import { waitForClientInitialized } from '@dynamic-labs-sdk/client';

    await waitForClientInitialized();
    // The client is ready — safe to call other SDK functions.
    ```
  </Tab>

  <Tab title="React">
    ```tsx theme={"system"}
    import { useInitStatus } from '@dynamic-labs-sdk/react-hooks';

    export function App() {
      const { data: initStatus } = useInitStatus();

      if (initStatus === 'uninitialized' || initStatus === 'in-progress') {
        return <LoadingState />;
      }

      if (initStatus === 'failed') {
        return <ErrorState />;
      }

      return <YourApp />;
    }
    ```
  </Tab>
</Tabs>

## From events to promises

This is the most important shift. The React SDK was **reactive**: you subscribed to events (`onAuthSuccess`, `onWalletAdded`, …) and the SDK told you when things happened. The JavaScript SDK is **imperative**: you call a function, and you get the result back directly — from the resolved promise (vanilla) or the hook's `onSuccess`/`onError` callbacks (React). You already know when a flow starts and finishes because you started it.

Events still exist for genuinely ambient state changes (a wallet added, a session expiring), but they're no longer how you observe the outcome of an action you triggered.

| React SDK event                                           | JavaScript SDK equivalent                                         |
| --------------------------------------------------------- | ----------------------------------------------------------------- |
| `onAuthInit`                                              | You call the auth function — you know when it starts              |
| `onAuthSuccess`                                           | The auth call's promise resolves (or hook `onSuccess`)            |
| `onAuthFailure`                                           | `try/catch` around the call (or hook `onError`)                   |
| `onAuthFlowCancel` / `onAuthFlowClose` / `onAuthFlowOpen` | You own the auth UI lifecycle directly                            |
| `onLogout`                                                | `onEvent({ event: 'logout' })`, or handle inline after `logout()` |
| `onUserProfileUpdate`                                     | `useUser()` reactivity / refresh after update                     |
| `onOtpVerificationResult`                                 | `verifyOTP()` resolves or rejects                                 |
| `onSignedMessage`                                         | `signMessage()` resolves with the signature                       |
| `onWalletAdded` / `onWalletRemoved`                       | `onEvent({ event: 'walletAccountsChanged' })`                     |
| `onWalletConnectionFailed`                                | `connectWithWalletProvider()` promise rejects                     |

### Subscribing to the events that remain

When you do want to react to ambient state changes, subscribe with `onEvent` (vanilla) or `useOnEvent` (React), which handles cleanup for you.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import { onEvent } from '@dynamic-labs-sdk/client';

    const unsubscribe = onEvent({
      event: 'walletAccountsChanged',
      listener: ({ walletAccounts }) => {
        renderWallets(walletAccounts);
      },
    });

    // Call unsubscribe() when you no longer need the listener.
    ```
  </Tab>

  <Tab title="React">
    ```tsx theme={"system"}
    import { useOnEvent } from '@dynamic-labs-sdk/react-hooks';

    function useWalletAccountsWatcher() {
      useOnEvent({
        event: 'walletAccountsChanged',
        listener: ({ walletAccounts }) => {
          renderWallets(walletAccounts);
        },
      });
    }
    ```
  </Tab>
</Tabs>

The core events include `initStatusChanged`, `initErrorChanged`, `userChanged`, `tokenChanged`, `sessionExpiresAtChanged`, `mfaTokenChanged`, `projectSettingsChanged`, `walletAccountsChanged`, and `logout`. The client emits additional events (for example around MFA, checkout, device registration, wallet providers, and flow execution) that follow the same subscribe pattern.

## RPC URL overrides

`overrideNetworkRpcUrl` is gone. Configure RPC URLs (and any other network overrides) with a `networksData` transformer when you create the client. It receives the full list of networks and returns the list the SDK should use, so you can rewrite, filter, or reorder them in one place — the first network of a chain is the default for wallets with no saved selection, so reordering also sets that default.

```typescript theme={"system"}
const dynamicClient = createDynamicClient({
  environmentId: 'YOUR_ENVIRONMENT_ID',
  transformers: {
    networksData: (networks) =>
      networks.map((network) =>
        network.networkId === '1'
          ? { ...network, rpcUrls: { http: ['https://your-rpc.example/eth'] } }
          : network,
      ),
  },
});
```

See [Network transformers](/docs/javascript/reference/networks/network-transformers) for the full API.

<Warning>
  Ethers.js is not supported. Migrate wallet/provider code to viem, which the EVM extension returns directly.
</Warning>

## See also

* [Creating a Dynamic client](/docs/javascript/reference/client/create-dynamic-client)
* [Initializing the client](/docs/javascript/reference/client/initialize-dynamic-client)
* [How React hooks map to client functions](/docs/javascript/reference/react-hooks-guide)
* [Non-wallet authentication](/docs/javascript/migrating-from-react/non-wallet-authentication) — the first flow to migrate
