React
Integrate XRPL-Connect into your React application with hooks.
Installation
npm install xrpl-connect xrplBasic Setup
Here's a minimal React component using XRPL-Connect:
import { useEffect, useRef, useState } from 'react';
import { WalletManager,XamanAdapter,CrossmarkAdapter } from 'xrpl-connect';
function WalletConnector() {
const connectorRef = useRef(null);
const [walletManager, setWalletManager] = useState(null);
const [account, setAccount] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
// Initialize WalletManager
const manager = new WalletManager({
adapters: [
new XamanAdapter({ apiKey: 'YOUR_API_KEY' }),
new CrossmarkAdapter(),
],
network: 'testnet',
autoConnect: true,
});
// Set up event listeners
manager.on('connect', (acc) => {
setAccount(acc);
setError(null);
});
manager.on('disconnect', () => {
setAccount(null);
});
manager.on('error', (err) => {
setError(err.message);
});
setWalletManager(manager);
// Connect component to manager
if (connectorRef.current) {
connectorRef.current.setWalletManager(manager);
}
// Cleanup
return () => {
manager.disconnect();
};
}, []);
const handleDisconnect = async () => {
if (walletManager) {
await walletManager.disconnect();
}
};
return (
<div>
<xrpl-wallet-connector
ref={connectorRef}
primary-wallet="xaman"
/>
{error && <p style={{ color: 'red' }}>Error: {error}</p>}
{account && (
<div style={{ marginTop: '20px', padding: '15px', background: '#f5f5f5', borderRadius: '8px' }}>
<h3>Connected Account</h3>
<p><strong>Address:</strong> {account.address}</p>
<p><strong>Network:</strong> {account.network.name}</p>
<button onClick={handleDisconnect}>Disconnect</button>
</div>
)}
</div>
);
}
export default WalletConnector;Creating a Custom Hook
For better reusability, create a custom hook:
import { useEffect, useRef, useState } from 'react';
import { WalletManager } from 'xrpl-connect';
function useWalletManager(adapters, network = 'testnet') {
const [walletManager, setWalletManager] = useState(null);
const [account, setAccount] = useState(null);
const [connected, setConnected] = useState(false);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(true);
const connectorRef = useRef(null);
useEffect(() => {
const manager = new WalletManager({
adapters,
network,
autoConnect: true,
});
manager.on('connect', (acc) => {
setAccount(acc);
setConnected(true);
setError(null);
});
manager.on('disconnect', () => {
setAccount(null);
setConnected(false);
});
manager.on('error', (err) => {
setError(err);
});
setWalletManager(manager);
setLoading(false);
if (connectorRef.current) {
connectorRef.current.setWalletManager(manager);
}
return () => {
manager.disconnect();
};
}, [adapters, network]);
const disconnect = async () => {
if (walletManager) {
await walletManager.disconnect();
}
};
return {
walletManager,
account,
connected,
error,
loading,
connectorRef,
disconnect,
};
}
export default useWalletManager;Then use it in your component:
import useWalletManager from './useWalletManager';
import { XamanAdapter } from 'xrpl-connect';
function App() {
const { account, connected, connectorRef, disconnect } = useWalletManager([
new XamanAdapter({ apiKey: 'YOUR_API_KEY' }),
]);
return (
<div>
<xrpl-wallet-connector ref={connectorRef} />
{connected && <button onClick={disconnect}>Disconnect</button>}
</div>
);
}Signing Transactions
Sign transactions with React:
import { useState } from 'react';
function PaymentForm({ walletManager }) {
const [loading, setLoading] = useState(false);
const [result, setResult] = useState(null);
const [error, setError] = useState(null);
const handleSubmit = async (e) => {
e.preventDefault();
if (!walletManager?.connected) {
setError('Wallet not connected');
return;
}
setLoading(true);
setError(null);
try {
const result = await walletManager.signAndSubmit({
TransactionType: 'Payment',
Account: walletManager.account.address,
Destination: 'rN7n7otQDd6FczFgLdlqtyMVrn3HMfXoQT',
Amount: '1000000',
});
setResult(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
return (
<form onSubmit={handleSubmit}>
<button type="submit" disabled={loading || !walletManager?.connected}>
{loading ? 'Sending...' : 'Send Payment'}
</button>
{error && <p style={{ color: 'red' }}>{error}</p>}
{result && <p style={{ color: 'green' }}>Success! Hash: {result.hash}</p>}
</form>
);
}Context API Pattern
For larger apps, use React Context to share wallet state:
import { createContext, useContext, useEffect, useState } from 'react';
import { WalletManager } from 'xrpl-connect';
const WalletContext = createContext();
export function WalletProvider({ children, adapters, network = 'testnet' }) {
const [walletManager, setWalletManager] = useState(null);
const [account, setAccount] = useState(null);
const [connected, setConnected] = useState(false);
useEffect(() => {
const manager = new WalletManager({
adapters,
network,
autoConnect: true,
});
manager.on('connect', (acc) => {
setAccount(acc);
setConnected(true);
});
manager.on('disconnect', () => {
setAccount(null);
setConnected(false);
});
setWalletManager(manager);
return () => manager.disconnect();
}, [adapters, network]);
return (
<WalletContext.Provider value={{ walletManager, account, connected }}>
{children}
</WalletContext.Provider>
);
}
export function useWallet() {
const context = useContext(WalletContext);
if (!context) {
throw new Error('useWallet must be used within WalletProvider');
}
return context;
}Usage:
import { WalletProvider, useWallet,XamanAdapter } from './WalletContext';
function App() {
return (
<WalletProvider adapters={[new XamanAdapter({ apiKey: 'YOUR_API_KEY' })]}>
<YourApp />
</WalletProvider>
);
}
function YourApp() {
const { account, connected } = useWallet();
return (
<div>
{connected && <p>Connected: {account.address}</p>}
</div>
);
}Error Handling
Handle errors gracefully:
function WalletError({ error }) {
if (!error) return null;
let message = error.message;
if (error.code === 'WALLET_NOT_FOUND') {
message = 'Please install a wallet to continue';
} else if (error.code === 'CONNECTION_FAILED') {
message = 'Failed to connect. Please try again.';
} else if (error.code === 'SIGN_FAILED') {
message = 'You rejected the transaction';
}
return (
<div style={{ padding: '10px', background: '#fee', color: '#c00', borderRadius: '4px' }}>
{message}
</div>
);
}TypeScript Support
XRPL-Connect has full TypeScript support:
import { useEffect, useRef, useState } from 'react';
import { WalletManager, Account, WalletError, XamanAdapter} from 'xrpl-connect';
interface WalletState {
walletManager: WalletManager | null;
account: Account | null;
connected: boolean;
error: WalletError | null;
}
function WalletConnector(): JSX.Element {
const connectorRef = useRef<HTMLElement>(null);
const [state, setState] = useState<WalletState>({
walletManager: null,
account: null,
connected: false,
error: null,
});
useEffect(() => {
const manager = new WalletManager({
adapters: [new XamanAdapter({ apiKey: 'YOUR_API_KEY' })],
network: 'testnet',
autoConnect: true,
});
manager.on('connect', (account: Account) => {
setState((prev) => ({
...prev,
account,
connected: true,
error: null,
}));
});
manager.on('error', (error: WalletError) => {
setState((prev) => ({ ...prev, error }));
});
setState((prev) => ({ ...prev, walletManager: manager }));
if (connectorRef.current) {
connectorRef.current.setWalletManager(manager);
}
}, []);
return (
<div>
<xrpl-wallet-connector ref={connectorRef} />
{state.error && <p>Error: {state.error.message}</p>}
{state.connected && <p>Connected: {state.account?.address}</p>}
</div>
);
}
export default WalletConnector;Best Practices
Memoize Adapters - Use
useMemoto prevent unnecessary recreation:jsxconst adapters = useMemo( () => [new XamanAdapter({ apiKey: 'YOUR_API_KEY' })], [] );Cleanup on Unmount - Always call
disconnect()in cleanup functionsUse TypeScript - Full type support available
Context for Global State - Use React Context for wallet state across components
Error Boundaries - Wrap wallet components in error boundaries
Test Event Listeners - Test with different wallets to ensure events fire
Debounce Event Handlers - Consider debouncing rapid state changes
Common Patterns
Gated Content
function GatedFeature() {
const { connected, account } = useWallet();
if (!connected) {
return <p>Please connect your wallet to use this feature</p>;
}
return <p>Welcome {account?.address}</p>;
}Auto-Connect Hook
function useAutoConnect() {
const { walletManager, connected } = useWallet();
const handleAutoConnect = async () => {
if (!connected && walletManager?.adapters.length) {
try {
// Try first available wallet
await walletManager.adapters[0].connect?.();
} catch (error) {
console.error('Auto-connect failed:', error);
}
}
};
return { handleAutoConnect };
}