Connecting to Phantom Wallets
The React SDK follows a clear connection pattern using hooks for wallet connection and chain-specific operations.
Connection Flow
- Provider Setup: Wrap your app with
PhantomProvider
- Connection: Use
useConnect()
to establish wallet connection
- Chain Operations: Use chain-specific hooks (
useSolana()
, useEthereum()
) for transactions and signing
import { useConnect, useSolana, useEthereum } from "@phantom/react-sdk";
function WalletExample() {
const { connect } = useConnect();
const { solana } = useSolana();
const { ethereum } = useEthereum();
// 1. Connect first
const handleConnect = async () => {
await connect();
};
// 2. Then use chain-specific operations
const sendSolanaTransaction = async () => {
const result = await solana.signAndSendTransaction(transaction);
};
const sendEthereumTransaction = async () => {
const result = await ethereum.sendTransaction(transaction);
};
}
Core Connection Hooks
useConnect
Connect to wallet:
import { useConnect } from "@phantom/react-sdk";
function ConnectButton() {
const { connect, isConnecting, error } = useConnect();
const handleConnect = async () => {
try {
const { walletId, addresses } = await connect();
console.log("Connected addresses:", addresses);
} catch (err) {
console.error("Failed to connect:", err);
}
};
return (
<button onClick={handleConnect} disabled={isConnecting}>
{isConnecting ? "Connecting..." : "Connect Wallet"}
</button>
);
}
useAccounts
Get connected wallet addresses:
import { useAccounts } from "@phantom/react-sdk";
function WalletAddresses() {
const addresses = useAccounts();
if (!addresses) {
return <div>Not connected</div>;
}
return (
<div>
{addresses.map((addr, index) => (
<div key={index}>
<strong>{addr.addressType}:</strong> {addr.address}
</div>
))}
</div>
);
}
useDisconnect
Disconnect from wallet:
import { useDisconnect } from "@phantom/react-sdk";
function DisconnectButton() {
const { disconnect, isDisconnecting } = useDisconnect();
return (
<button onClick={disconnect} disabled={isDisconnecting}>
{isDisconnecting ? "Disconnecting..." : "Disconnect"}
</button>
);
}
useIsExtensionInstalled
Check if the Phantom browser extension is installed (for injected provider):
import { useIsExtensionInstalled } from "@phantom/react-sdk";
function ExtensionStatus() {
const { isLoading, isInstalled } = useIsExtensionInstalled();
if (isLoading) {
return <div>Checking for Phantom extension...</div>;
}
return (
<div>
{isInstalled ? (
<p>✅ Phantom extension is installed!</p>
) : (
<p>
❌ Phantom extension not found.{" "}
<a href="https://phantom.app/download" target="_blank">
Install here
</a>
</p>
)}
</div>
);
}
Provider Type Switching
Even if you start with the embedded provider, you can switch to the injected provider (browser extension) at runtime using the underlying SDK:
import { useConnect, usePhantom, useIsExtensionInstalled } from "@phantom/react-sdk";
function ProviderSwitcher() {
const { connect } = useConnect();
const { sdk } = usePhantom();
const { isInstalled, isLoading } = useIsExtensionInstalled();
const switchToExtension = async () => {
if (!isInstalled) {
alert("Phantom extension is not installed");
return;
}
// Switch to injected provider and connect
await sdk.switchProvider("injected");
const { addresses } = await connect();
console.log("Connected to extension:", addresses);
};
if (isLoading) {
return <div>Checking for Phantom extension...</div>;
}
return (
<button onClick={switchToExtension} disabled={!isInstalled}>
{isInstalled ? "Switch to Extension" : "Extension Not Installed"}
</button>
);
}
Future API Changes: This interface will change in future versions. The connect()
method will receive a parameter with the provider type, eliminating the need for switchProvider()
.
Important notes about redirectUrl
(for embedded provider):
- Must be an existing page/route in your application
- Must be whitelisted in your Phantom Portal app configuration
- This is where users will be redirected after completing OAuth with Google