React Native & Expo
@layerhop/react-native — the swap widget and checkout as React Native components (react-native-webview) with typed callbacks, wallet deep-link helpers, and notes on using the SDK in React Native.
@layerhop/react-native puts the LayerHop swap widget and the hosted checkout into React Native and Expo apps. <LayerHopWidget /> and <LayerHopCheckout /> load the hosted pages in a react-native-webview and take the same options and typed callbacks as the web widget. Wallet links open the user's wallet app. For a fully custom UI, the TypeScript SDK and the React hooks run in React Native too.
Nothing here holds keys. The user connects their own wallet app (WalletConnect or a wallet deep link) and signs there.
How do I install it?#
# Expo
npx expo install @layerhop/react-native react-native-webview
# bare React Native
npm install @layerhop/react-native react-native-webview && npx pod-installPeers: react >= 18, react-native >= 0.72, react-native-webview >= 13. Embed URLs are built with the standard URL API: Expo SDK 50+ includes it. In a bare app on an older React Native, install react-native-url-polyfill and add import "react-native-url-polyfill/auto"; at the top of your entry file. Without it, the components throw a LayerHopWidgetError that says so.
Swap widget#
import { LayerHopWidget } from "@layerhop/react-native";
export function SwapScreen() {
return (
<LayerHopWidget
srcChain="base" srcToken="USDC" dstChain="solana" dstToken="SOL"
theme="dark" preset="midnight" gasOnArrival
integrator="myapp" // your partner id: earns your partner fee
onQuote={(q) => console.log(q.provider, q.dstAmount)}
onSubmitted={(e) => console.log("sent", e.srcTxHash)}
onStatus={(e) => console.log("order", e.state)}
onError={(e) => console.warn(e.code, e.message)}
/>
);
}Every widget option works, and so does every callback: onReady, onQuote, onRoutes, onSubmitted, onStatus, onError, onResize, onWalletConnected, onWalletDisconnected, onAmountChanged and onEvent. Invalid options throw a LayerHopWidgetError during render. Changing an option reloads the page, while callbacks can change freely.
| React Native prop | Default | What it does |
|---|---|---|
baseUrl |
https://layerhop.com |
Where /embed is served. |
autoResize |
true |
Follow the page's height. Set false and give style a size ({ flex: 1 }) for a full-screen widget. |
height |
640 |
Height before the first resize event, or always when autoResize is off. |
style |
Style of the wrapping View. |
|
onExternalLink |
Called before a wallet link or external page opens. Return false to handle it yourself. |
|
webViewProps |
Extra react-native-webview props. The bridge, source and navigation handling stay in place. |
Checkout#
Create the session on your server with your API key (lh.checkout.create), send its id to the app, then render:
import { LayerHopCheckout } from "@layerhop/react-native";
<LayerHopCheckout
sessionId={sessionId}
onStatus={(status) => console.log(status)} // open → processing → paid
onPaid={(session) => navigation.navigate("Thanks", { id: session.id })}
onError={(err) => Alert.alert("Payment unavailable", err.message)}
/>;Fulfil the order on the checkout.session.completed webhook, not only on onPaid. Other options:
productId="prod_…", with optionalreferenceandmetadata, starts a session on mount through the public endpoint. If the product only allows certain web origins, this answersFORBIDDEN.mode="modal"makes the page add a close button. Render the component inside a React Native<Modal>and close the modal inonClose, which also fires afteronCancel.autoResize,height,style,renderLoading,onExternalLinkandwebViewPropswork as they do on the widget.
How do wallet deep links work?#
When the user connects or signs, the page inside the WebView hands off to a wallet app. It does this through a custom scheme (wc:, metamask://, phantom://) or a universal link (https://metamask.app.link/…). The components open these links with Linking and keep the WebView on the LayerHop page. Explorer links and other pages outside LayerHop open in the system browser. Sub-frames, such as wallet SDK iframes, load normally.
import { createReturnUrl, openWalletLink, useWalletReturn } from "@layerhop/react-native";
// Fires once the user comes back from the wallet app, or when a wallet opens your app through its scheme.
useWalletReturn((e) => console.log("back from wallet", e.reason, e.url), { scheme: "myapp" });
createReturnUrl("myapp", "swap", { order: "q_1" }); // "myapp://swap?order=q_1"
await openWalletLink("metamask://wc?uri=…"); // false when no app can open itThe scheme is your app's own: expo.scheme in app.json, or the URL types and intent filters of a bare app.
Can I use the SDK and hooks in React Native?#
Yes. @layerhop/sdk and @layerhop/react use only fetch, URLSearchParams and AbortController, and React Native has all three. Live streams use Server-Sent Events: streamOrder, checkout.stream and useOrderStream. React Native's fetch can't stream a response body, so the SDK (0.6+) reads these streams with XMLHttpRequest progress events in React Native. It does this automatically. To force it, use new LayerHop({ streaming: "xhr" }). To use Expo's streaming fetch instead, pass it in with new LayerHop({ fetch }), importing fetch from expo/fetch.
Never ship an API key in an app. Public calls (quotes, transactions, order status) don't need one. Use your key on your server for anything else, including creating checkout sessions and verifying webhooks.
How does it work?#
The hosted pages post { source: "layerhop", type, payload } (widget) and { source: "layerhop-checkout", … } (checkout) to window.parent, the same postMessage protocol the web embed uses. Before the page loads, the component injects a small script. That script gives the page a window.parent that forwards only those envelopes to React Native. The component accepts messages only from the page's own origin and validates them with the same parsers as @layerhop/widget. The web widget's live setParams updates aren't used here: changing an option reloads the page.
Last updated