@buckspay/react-native brings the SDK to iOS and Android. The core pipeline (auth-entry build,
fee-bump, prepare -> sign -> send) is platform-agnostic. This package adds three things on top:
nativePasskey- aWebAuthnLikebacked byreact-native-passkeythat drives the device secure enclave instead ofnavigator.credentials. The private key never leaves the device.- Secure-storage adapters -
memorySecureStore,expoSecureStore,keychainSecureStorefor persisting session blobs and credential IDs. - Hermes polyfills - installed as a side-effect on import, before
@stellar/stellar-sdkis used.
BuckspayProvider, useWallet, and useStellarPay are re-exported by reference from
@buckspay/react - the same store, same status machine, zero fork. Your screen logic is
identical to a web component; only the host primitives (<View> / <Text> / <Pressable>)
and the signer differ.
pnpm add @buckspay/react-native @buckspay/react @buckspay/core @buckspay/accounts @buckspay/relayer
# Native peers - install in the app, not in the SDK
pnpm add react-native react-native-passkey react-native-get-random-values
The peer modules (
react-native-passkey, react-native-get-random-values) are optional: each
is lazily loaded only if you use the feature that depends on it, so apps that skip nativePasskey
never pull in the native module.import { nativePasskey } from "@buckspay/react-native";
import { ozContractAccount } from "@buckspay/accounts/oz-contract";
import { buckspayFacilitator } from "@buckspay/relayer/buckspay-facilitator";
import type { BuckspayConfig } from "@buckspay/core";
const config: BuckspayConfig = {
network: "testnet",
account: ozContractAccount({ network: "testnet", sponsorAddress: "GA...SPONSOR" }),
signer: nativePasskey({ rpId: "app.example.com", rpName: "My App" }),
relayer: buckspayFacilitator({ url: "/api/gasless", network: "testnet" }),
gas: { mode: "sponsored" }
};
rpIdstringrpNamestringimport { BuckspayProvider } from "@buckspay/react-native";
import { createRpcSimContext } from "@buckspay/core";
export function App() {
return (
<BuckspayProvider config={config} sim={createRpcSimContext("https://soroban-testnet.stellar.org")}>
{/* your screens */}
</BuckspayProvider>
);
}
useWallet and useStellarPay work exactly as on web. Replace DOM elements with React
Native primitives:import { useWallet, useStellarPay } from "@buckspay/react-native";
import { View, Text, Pressable } from "react-native";
function PayScreen() {
const { wallet, connect, status } = useWallet();
const { pay, status: payStatus, receipt } = useStellarPay();
if (!wallet) {
return <Pressable onPress={() => void connect()} disabled={status === "connecting"}>
<Text>{status === "connecting" ? "Connecting..." : "Connect"}</Text>
</Pressable>;
}
return (
<View>
<Pressable onPress={() => void pay([/* calls */])} disabled={payStatus === "relaying"}>
<Text>Pay 1.50 USDC (free)</Text>
</Pressable>
{receipt && <Text>Settled: {receipt.transferTx}</Text>}
</View>
);
}
Secure storage
Session blobs and credential IDs are persisted via aSecureStore port. Three adapters are
exported from @buckspay/react-native:
| Adapter | Use |
|---|---|
memorySecureStore() | Tests and ephemeral in-memory sessions |
expoSecureStore() | Expo managed workflow (expo-secure-store) |
keychainSecureStore({ service }) | Bare React Native (react-native-keychain) |
Full example
Next
Native passkey signer
How
nativePasskey delegates to the shared passkey crypto pipeline.Web - @buckspay/react
The web counterpart - same hooks, browser authenticator transport.

