Skip to main content

Recommended: JavaScript SDK for React Native

The JavaScript SDK is now the default and recommended way to build React Native apps with Dynamic. If you are starting a new app, see the React Native Quickstart (JS SDK). This guide is for migrating an existing legacy React Native SDK app.

Who this is for

You’re moving a React Native app from the legacy SDK (@dynamic-labs/legacy-client + @dynamic-labs/legacy-react-native-extension) to the headless JavaScript SDK (@dynamic-labs-sdk/*). The same JavaScript SDK packages run on web and React Native, so the migration is mostly a shift from a webview-based, UI-heavy SDK to a headless, function-based one. The JavaScript SDK is headless: it gives you functions and React hooks for auth, wallets, and money movement, and you build every screen. The legacy SDK shipped a built-in WebView UI and orchestrated flows for you.
Do not install @dynamic-labs/legacy-client or @dynamic-labs/legacy-react-native-extension for new code. They are the legacy React Native SDK and have a different API.

The big shifts

  • No built-in UI. client.ui.*, the Dynamic WebView widget, and all modals are gone. You render every screen.
  • No ReactNativeExtension or webview URL. Use createDynamicClient exactly as on web.
  • Extensions are added separately, not chained. createClient().extend(ReactNativeExtension()).extend(ViemExtension()) becomes createDynamicClient() followed by addEvmExtension(), addSolanaExtension(), etc.
  • React hooks return a TanStack Query result. Destructure .data, e.g. const { data: user } = useUser().
  • Events are for ambient state, not flow outcomes. Old client.<area>.on/off becomes onEvent / useOnEvent.
  • External wallets connect through WalletConnect. There is no built-in wallet list; you build it and deep-link users to wallet apps.
  • Post-login setup is explicit. WaaS wallet creation, MFA, and device registration no longer happen automatically.
  • Native-specific setup is small. Polyfills, deep-link schemes, and the WalletConnect native shim are the main additions. See React Native Setup for details, then Expo or Bare React Native depending on how your app is built.

Type renames

Client initialization

Legacy SDK

JavaScript SDK

An embedded wallet signs inside a WebView that the SDK loads with metadata.universalLink as its host origin, so that exact value must be listed under Allowed Origins in your environment’s dashboard security settings. If it isn’t, signing fails while reads keep working — see React Native Setup for the exact error. Expo Go is not supported; use a development build or a bare React Native workflow.
Add the extensions for every chain you support. Chain extensions take no arguments and register against the default client.
For WalletConnect, add addWalletConnectEvmExtension() / addWalletConnectSolanaExtension() from the respective /wallet-connect subpaths. See Authenticate with WalletConnect. In React, wrap the app with <QueryClientProvider> then <DynamicProvider client={dynamicClient}>:
For platform-specific setup — polyfills, native modules, deep-link schemes — see React Native Setup, then the guide for your build: Expo or Bare React Native.

Reading state

Events to promises

The legacy SDK was event-driven: you subscribed to onAuthSuccess, onWalletAdded, etc. The JavaScript SDK is imperative — you await the function or use a hook’s onSuccess/onError. Events still exist for ambient changes (wallet list changes, session expiry), but they are not how you observe an action you started.

Connecting external wallets

The legacy SDK rendered a “Connect Wallet” list inside the WebView. The JavaScript SDK ships no connect UI, so you build the list. On React Native, external wallets are reached through WalletConnect deep links (or QR on tablet/desktop).
connectAndVerifyWithWalletConnectEvm over a deep link is not supported on React Native because iOS blocks a second deeplink not tied to a user gesture. For deeplink connections, call connectWithWalletConnectEvm first, then verify the account from a separate user tap with verifyWalletAccount. QR-based connectAndVerify works.
See Authenticate with WalletConnect for the full flow and walletConnectUserActionRequested handling.

API mapping

Client and extensions

Authentication

All new functions are from @dynamic-labs-sdk/client unless noted.
OTP is stateless now. sendEmailOTP and sendSmsOTP return an otpVerification object; pass it to verifyOTP along with the user’s code as verificationToken.
updateUser({ userFields }) may return an OTPVerification if the changed email or phone requires verification. Hold it and finish with verifyOTP.

MFA and step-up authentication

Device registration

Wallets

WaaS (embedded wallets)

From @dynamic-labs-sdk/client/waas unless noted.
<ExportPrivateKey /> renders the key in a sandboxed WebView and never exposes it to JS. It fills the container you size, so give it a real height (for example, inside a flex-1 Modal).

Chains

Gaps and changed APIs

A small number of legacy APIs have no direct equivalent in the headless SDK. In most cases you replace them with a combination of available functions or remove the feature.
All @dynamic-labs-sdk/* packages publish in lockstep. Pin every package to the same minor version or the SDK’s runtime version assertion will throw. Mixed versions are the most common source of “function not found” errors after a bump.

Version and dependency notes

  • Keep all @dynamic-labs-sdk/* packages on the same minor version.
  • WalletConnect on React Native needs @walletconnect/react-native-compat (Expo: npx expo install @walletconnect/react-native-compat; bare: also install @react-native-community/netinfo). Import it as the first line of your polyfills file.
  • Passkeys need react-native-passkey and platform-specific configuration. See Passkeys on React Native.

Next steps

Last modified on October 26, 2026