| description | Integrate XRPL Connect v1.0 with React and Next.js using the official provider, hooks, and connector. |
|---|
Use the official React bindings instead of building a custom context around the web component.
These commands select the prerelease channel. See release-channel guidance for switching the SDK and React binding to stable v1 after publication.
pnpm add xrpl-connect@rc @xrpl-commons/xrpl-connect-react@rc xrpl@^4 react react-domCreate the adapter configuration once, outside component render. The provider snapshots its initial configuration and owns one manager; changing the object does not rebuild it. Give the provider a new React key only when you intentionally want to replace that manager. This Vite example uses client-visible VITE_* variables:
import { XrplConnectProvider, WalletConnector } from '@xrpl-commons/xrpl-connect-react';
import {
CrossmarkAdapter,
MetaMaskSnapAdapter,
WalletConnectAdapter,
XamanAdapter,
} from 'xrpl-connect';
const config = {
adapters: [
new XamanAdapter({ apiKey: import.meta.env.VITE_XAMAN_API_KEY }),
new CrossmarkAdapter(),
new WalletConnectAdapter({
projectId: import.meta.env.VITE_WALLETCONNECT_PROJECT_ID,
}),
new MetaMaskSnapAdapter(),
],
network: 'testnet' as const,
autoConnect: true,
};
export function App() {
return (
<XrplConnectProvider config={config}>
<Header />
<WalletConnector
wallets={['xaman', 'crossmark', 'walletconnect', 'metamask-snap']}
showUnavailable
theme="dark"
onError={(error) => console.error(error.code, error.message)}
/>
</XrplConnectProvider>
);
}useWallet() provides the stable manager, reactive state, and direct connect/disconnect actions.
import { useWallet, useWalletModal } from '@xrpl-commons/xrpl-connect-react';
function Header() {
const { connected, connecting, account, network, error, disconnect } = useWallet();
const { ready, open } = useWalletModal();
if (!connected || !account) {
return (
<button onClick={() => void open()} disabled={!ready || connecting}>
Connect wallet
</button>
);
}
return (
<div>
<span>{account.address}</span>
<span>{network?.name}</span>
{error && <span role="alert">{error.message}</span>}
<button onClick={() => void disconnect()}>Disconnect</button>
</div>
);
}The hook returns manager, connected, account, network, connecting, error, connect, and disconnect.
import { WalletErrorCode, isWalletError } from 'xrpl-connect';
import { useSigner, useWallet } from '@xrpl-commons/xrpl-connect-react';
function PaymentButton({ destination }: { destination: string }) {
const { account } = useWallet();
const { signAndSubmit } = useSigner();
const pay = async () => {
if (!account) return;
try {
const result = await signAndSubmit({
TransactionType: 'Payment',
Account: account.address,
Destination: destination,
Amount: '1000000',
});
console.log(result.hash);
} catch (error) {
if (isWalletError(error) && error.code === WalletErrorCode.SIGN_REJECTED) return;
throw error;
}
};
return (
<button onClick={() => void pay()} disabled={!account}>
Pay 1 XRP
</button>
);
}useSigner() exposes sign, signAndSubmit, and signMessage. Check manager.supports('signMessage') before offering arbitrary message signing.
useWalletModal() returns reactive ready, open(): Promise<void>,
openAndWait(): Promise<AccountInfo>, and close(): void. Await open() to observe availability
failures, or await openAndWait() when the caller needs the connected account; openAndWait()
also rejects if the modal closes before a connection completes.
ready becomes true after a connector registers and returns to false after the last connector
unmounts. Calling open() or openAndWait() while it is false rejects with a namespaced setup
error. With multiple connectors, the newest registration owns modal calls; unmounting it falls
back to the previous connector. close() is a safe no-op when none is registered.
WalletConnector accepts:
primaryWalletand orderedwalletsshowUnavailableto include Install or disabled Unavailable wallet rowstheme:dark,light, orpurple- typed
--xc-*values throughcssVars className,style,onConnecting,onConnect, andonError- standard host attributes such as
id,title,data-*, andaria-* - a typed
WalletConnectorElementref exposingopen(),openAndWait(),close(), andtoggle()
import { useRef } from 'react';
import { WalletConnector, type WalletConnectorElement } from '@xrpl-commons/xrpl-connect-react';
function WalletModal() {
const connectorRef = useRef<WalletConnectorElement>(null);
return (
<WalletConnector
ref={connectorRef}
id="wallet-modal"
aria-label="Choose a wallet"
data-testid="wallet-connector"
/>
);
}Explicit primaryWallet, wallets, and className values take precedence over overlapping raw
host attributes. For each CSS property, style takes precedence over cssVars, which takes
precedence over the selected theme.
Unavailable wallets are hidden by default. The camelCase showUnavailable prop maps to the native
show-unavailable boolean attribute; setting the prop to false removes the attribute again.
The package is safe to import during server rendering. Provider and wallet UI usage still require a client boundary because they use browser wallet APIs:
'use client';
import { XrplConnectProvider, WalletConnector } from '@xrpl-commons/xrpl-connect-react';
import type { ReactNode } from 'react';
import { WalletConnectAdapter, XamanAdapter } from 'xrpl-connect';
const config = {
adapters: [
new XamanAdapter({ apiKey: process.env.NEXT_PUBLIC_XAMAN_API_KEY! }),
new WalletConnectAdapter({
projectId: process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID!,
}),
],
network: 'testnet' as const,
};
export function WalletProviders({ children }: { children: ReactNode }) {
return (
<XrplConnectProvider config={config}>
{children}
<WalletConnector />
</XrplConnectProvider>
);
}Expose browser identifiers with NEXT_PUBLIC_*; do not expose secrets. Dynamic import with ssr: false is optional for route-level code splitting, not required to make the package importable.
The provider owns one manager, subscribes before auto-connect can update state, and cancels owned pending connections on unmount. Do not add a second custom context or duplicate manager listeners around it.
See transactions and signing, production and security, and the runnable examples/react application.