To use our SDK with React Native, you only need to check the nativeEvent field from the WebViewMessageEvent, which contains data about the specific event. See the example below:
import { useEffect, useRef, useState } from "react";
import { Linking } from "react-native";
import { SafeAreaProvider, SafeAreaView } from "react-native-safe-area-context";
import { WebView, WebViewMessageEvent } from "react-native-webview";
const urlMapper = {
cert: {
sdkMobile: "https://sdk-mobile.cert.zerohash.com",
zeroHashAppsURL: "https://web-sdk.cert.zerohash.com",
},
prod: {
sdkMobile: "https://sdk-mobile.zerohash.com",
zeroHashAppsURL: "https://web-sdk.zerohash.com",
},
};
/**
* List of permissions used to request a JWT in the `client_auth_token` request
*/
const permissions = [
"crypto-buy",
"crypto-sell",
"crypto-withdrawals",
"crypto-payouts",
"fiat-deposits",
"fiat-withdrawals",
"fwc", // Fund
"onboarding",
"participant-profile",
"update-participant",
"crypto-account-link",
];
/**
* You can copy and paste a JWT on jwtToken for testing. You will need to
* implement a function to fetch the JWT from your backend and replace this variable.
* We have an example below using fetchJwt and setToken functions.
* */
const jwtToken = ``;
const App = () => {
const webViewRef = useRef<WebView>(null);
const [token, setToken] = useState<string>(jwtToken);
useEffect(() => {
fetchJwt();
}, []);
// Fetch JWT token from server
const fetchJwt = async () => {
const jwtPayload = {
// The participant code you want to request the JWT for.
participant_code: "<PARTICIPANT_CODE>",
// The permission required for your use-case, refer to Permissions for
// the allowed values
permissions: ["fwc"],
};
try {
const response = await fetch(`<API_URL_TO_FETCH_TOKEN>`, {
method: "POST",
body: JSON.stringify(jwtPayload),
});
const parsedResponse = await response.json();
setToken(parsedResponse.message.token);
} catch (e) {
console.error("could not fetch jwt", e);
}
};
// Open modal using injected JS script in webview
const openModal = () => {
// Replace "jwtToken" with "token" to use the token you got from your backend
webViewRef.current?.injectJavaScript(
`window.postMessage({ "type": "OPEN_MODAL", "payload": { "appIdentifier": "fund", "jwt": "${jwtToken}"}});true;`
);
};
const handleMessage = (event: WebViewMessageEvent) => {
try {
// Parse message from webview event data checking nativeEvent field
const parsedMessage = JSON.parse(event.nativeEvent.data);
console.log("Received message:", parsedMessage);
if (parsedMessage.type === "SDK_MOBILE_READY") {
console.log("SDK is ready, opening modal...");
openModal();
}
// Handle webview events
// Your code here...
} catch (e) {
alert(
`could not parse message: ${JSON.stringify(event.nativeEvent.data)}`
);
}
};
return (
<SafeAreaProvider>
<SafeAreaView style={{ flex: 1 }}>
<WebView
allowsInlineMediaPlayback={true}
ref={webViewRef}
onMessage={handleMessage}
source={{
// Double check the environment you are using and pick the correct URLs
// accordingly
uri: `${urlMapper.cert.sdkMobile}/v1?zeroHashAppsURL=${urlMapper.cert.zeroHashAppsURL}`,
}}
onNavigationStateChange={(event) => {
if (!event.url.startsWith(urlMapper.cert.sdkMobile)) {
webViewRef?.current.stopLoading();
Linking.openURL(event.url);
}
}}
/>
</SafeAreaView>
</SafeAreaProvider>
);
};
export default App;
Fiat Account link specific (Android and iOS) Updated 12/26/2025
For Android the experience at this phase will not redirect the user back to the Customer application until we add a custom
deeplinkthat shall be provided and configured internally. The work for this is ongoing and should be available soon.
Overview
This guide explains how to add special handling for Plaid authentication flows in your React Native app using an embedded browser. This provides a native iOS and Android authentication experience and automatically closes when the flow completes.
What This Does
- Detects when users navigate to plaid.com URLs in your WebView
- Launches native iOS
ASWebAuthenticationSessionorAndroid Embedded browserinstead of loading in WebView - Automatically closes the authentication session when it detects completion
- Provides better user experience with native iOS/Android authentication UI
Implementation Steps for expo
import * as WebBrowser from "expo-web-browser";
import { useRef } from "react";
import { Linking, Platform } from "react-native";
import { SafeAreaProvider, SafeAreaView } from "react-native-safe-area-context";
import { WebView } from "react-native-webview";
const urlMapper = {
cert: {
sdkMobile: "https://sdk-mobile.cert.zerohash.com",
zeroHashAppsURL: "https://web-sdk.cert.zerohash.com",
},
prod: {
sdkMobile: "https://sdk-mobile.zerohash.com",
zeroHashAppsURL: "https://web-sdk.zerohash.com",
},
};
const IOS_REDIRECT_URL = "zerohashapp://done";
WebBrowser.maybeCompleteAuthSession();
const App = () => {
const webViewRef = useRef<WebView>(null);
const launchPlaidAuth = async (url: string) => {
try {
if (Platform.OS === "ios") {
const result = await WebBrowser.openAuthSessionAsync(url, IOS_REDIRECT_URL);
} else {
const result = await WebBrowser.openAuthSessionAsync(url);
}
} catch (e) {
console.warn("External flow error", e);
}
};
return (
<SafeAreaProvider>
<SafeAreaView style={{ flex: 1 }}>
<WebView
allowsInlineMediaPlayback={true}
ref={webViewRef}
onMessage={handleMessage}
source={{
// Double check the environment you are using and pick the correct URLs
// accordingly
uri: `${urlMapper.cert.sdkMobile}/v1?zeroHashAppsURL=${urlMapper.cert.zeroHashAppsURL}`
}}
onShouldStartLoadWithRequest={(request) => {
const url = request.url || ""
if (url.includes("plaid.com")) { // Make sure to remove the "Platform.OS === 'ios' "from the previous guide that was intended for iOS only
launchPlaidAuth(url)
return false; // prevent in-WebView navigation
}
return true
}}
onNavigationStateChange={(event) => {
if (!event.url.startsWith(urlMapper.cert.sdkMobile)) {
webViewRef?.current.stopLoading()
Linking.openURL(event.url)
}
}}
/>
</SafeAreaView>
</SafeAreaProvider>
);
};
export default App
Even if you are not using Expo you should be able to adapt opening the ASWebAuthenticationSession/Android corresponding in your own structure.
