Skip to main content

Recommended: JavaScript SDK for React Native

While this SDK is still supported, we recommend using newer JavaScript SDK, which is optimized for React Native, but also comes with a host of other benefits.
The way Dynamic integrates with React Native is by extending our client with the React Native Extension. This adds the reactNative module, which provides access to a webview component that must be rendered to your app.
Since our client was built with a modular approach, each extension must be installed as a separate package, so to keep the client’s package size to a minimum.

Installation

From v4.83.0 onwards, the React Native SDK requires Expo SDK 52 or later. @dynamic-labs/react-native-extension declares expo-modules-core: >=2.0.0 as a peer dependency, which is first satisfied by Expo SDK 52. Stay on v4.82.x if you need to support Expo SDK 50 or 51.
Simply run the following in your terminal:
Shell

Prebuild (Expo) / Native linking

Since our SDK relies on native modules (e.g. react-native-webview, expo-secure-store), you need to rebuild the native layer of your app after installing.
Expo Go is not supported. Dynamic depends on native modules that are linked when you prebuild your app (for example with npx expo prebuild or an EAS/cloud build). Expo Go runs a generic prebuilt client and does not prebuild your project, so those native dependencies never make it into the binary you launch. Use a development build, EAS Build, or a bare React Native workflow instead.
If you’re using Expo with a managed workflow, run:
Shell
Then rebuild your app with npx expo run:ios or npx expo run:android.

Usage with React Native

First, extend your client with our extension:
Next, render the webview injected into the client by the extension:
You can read more about our react native package here.

Embedded WebView (native overlay)

Available from v4.82.0.
By default, the SDK renders its webview as a regular React Native component (via react-native-webview) that you mount in your app tree. If your app’s navigation aggressively unmounts and remounts screens — or you simply want the wallet session to be fully decoupled from your view tree — you can opt in to embedded WebView mode. Pass embeddedWebView: true to the extension:
In this mode the SDK hosts the webview-controller inside a dedicated native overlay window (WKWebView on iOS, android.webkit.WebView on Android) owned outside the React Native view tree. The overlay is created lazily on first use and retained for the process lifetime.
When embeddedWebView is true, do not render dynamicClient.reactNative.WebView in your app — it resolves to a no-op component on the embedded path.
On platforms other than iOS / Android (e.g. web), the embeddedWebView flag is ignored and the standard react-native-webview path is used.

Caveats

  • Same load engine as the standard path. The embedded overlay runs the same bounded-retry load engine as the react-native-webview path, so a transient failure is retried automatically and you can observe progress and trigger a manual retry through the same API — see Tuning and monitoring the WebView load. When every attempt is exhausted, the failure surfaces as core.initialization.error with WebViewFailedToLoadError; treat the SDK as un-initialised in your error handler.
  • Singleton. Re-invoking ReactNativeExtension({ embeddedWebView: true }) (e.g. when you recreate the client with a different environmentId) re-binds the JS bridge to the same native overlay rather than creating a new one.
  • Debugging still works via webviewDebuggingEnabled: true (Safari Web Inspector on iOS, chrome://inspect on Android).

Tuning and monitoring the WebView load

Whichever WebView path you use, the SDK loads its webview through a bounded-retry engine: each load attempt is given a time budget, a transient failure (network drop, wedged CDN edge) is retried automatically after a short backoff, and the failure only becomes terminal once every attempt is exhausted. You can tune the budgets, observe the load in real time, and trigger a manual retry.

Tuning the load budgets

Pass a webviewLoad group to the extension to tune how long each attempt may take and how many attempts to make. Every field is optional — only tune these for pathological networks.
A misconfigured value warns and is clamped into its safe range — it never throws, so a config typo cannot crash SDK initialisation.

Observing the load state

client.sdk.loadState is a live snapshot of the load engine. Read it through useReactiveClient to re-render on every change:
loadState has the following shape: Outside React, subscribe to the loadStateChanged event instead:

Retrying a failed load

When status is failed, call client.sdk.retry() to start a fresh load cycle. It resolves with the load state of the next ready or failed transition. Calling it while waiting_to_retry skips the remaining backoff and retries immediately; in any other state it is a safe no-op.
Set maxAttempts: 1 when you want your app to own the retry experience: the first failure becomes terminal immediately, and you drive recovery entirely through your own UI and client.sdk.retry().
Last modified on July 24, 2026