Framework Kit

@solana/react-hooks

React hooks for wallet connection, balances, and transactions

React bindings for @solana/client. Wrap your app once with SolanaProvider, then use hooks anywhere for wallets, balances, and transactions.

Installation

npm install @solana/client @solana/react-hooks
pnpm add @solana/client @solana/react-hooks
yarn add @solana/client @solana/react-hooks
bun add @solana/client @solana/react-hooks

Setup

1. Create a Client

import { autoDiscover, createClient } from "@solana/client";

const client = createClient({
  endpoint: "https://api.devnet.solana.com",
  walletConnectors: autoDiscover(),
});

2. Wrap with SolanaProvider

import { SolanaProvider } from "@solana/react-hooks";

export function App() {
  return (
    <SolanaProvider client={client}>
      {/* Your components */}
    </SolanaProvider>
  );
}

Wallet Hooks

useWalletConnection

Complete wallet connection management:

function WalletPanel() {
  const {
    connectors,      // Available wallet connectors
    connect,         // Connect to a wallet
    disconnect,      // Disconnect current wallet
    wallet,          // Current wallet session
    status,          // 'disconnected' | 'connecting' | 'connected'
    currentConnector // Current connected wallet info
  } = useWalletConnection();

  if (status === "connected") {
    return (
      <div>
        <p>Connected via {currentConnector?.name}</p>
        <p>{wallet?.account.address?.toString()}</p>
        <button onClick={disconnect}>Disconnect</button>
      </div>
    );
  }

  return connectors.map((c) => (
    <button key={c.id} onClick={() => connect(c.id)}>
      Connect {c.name}
    </button>
  ));
}

useWallet

Access wallet state:

function WalletInfo() {
  const { wallet, status, connectors } = useWallet();

  if (status !== "connected") return <p>Not connected</p>;
  return <p>Address: {wallet.account.address.toString()}</p>;
}

useWalletSession

Get the current wallet session for signing:

function SignButton() {
  const session = useWalletSession();

  if (!session) return <p>Connect wallet first</p>;
  return <p>Ready to sign with {session.account.address.toString()}</p>;
}

useWalletModalState

Manage wallet modal visibility:

function WalletModal() {
  const { isOpen, open, close } = useWalletModalState();

  return (
    <>
      <button onClick={open}>Connect Wallet</button>
      {isOpen && (
        <Modal onClose={close}>
          <WalletList />
        </Modal>
      )}
    </>
  );
}

Data Hooks

useBalance

Auto-fetch and watch lamport balance:

function BalanceCard({ address }: { address: string }) {
  const { lamports, fetching, slot, error } = useBalance(address);

  if (fetching) return <p>Loading...</p>;
  if (error) return <p>Error loading balance</p>;

  return (
    <p>
      Balance: {lamports?.toString() ?? "0"} lamports
      (slot {slot?.toString()})
    </p>
  );
}

useAccount

Auto-fetch and watch account data:

function AccountInfo({ address }: { address: string }) {
  const account = useAccount(address);

  if (!account || account.fetching) return <p>Loading...</p>;

  return (
    <div>
      <p>Lamports: {account.lamports?.toString() ?? "0"}</p>
      <p>Owner: {account.owner ?? "Unknown"}</p>
      <p>Slot: {account.slot?.toString()}</p>
    </div>
  );
}

useLookupTable

Fetch address lookup table data:

function LookupTableInfo({ address }: { address: string }) {
  const { data, isLoading, error } = useLookupTable(address);

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Error loading LUT</p>;

  return (
    <div>
      <p>Addresses: {data?.addresses.length ?? 0}</p>
      <p>Authority: {data?.authority ?? "None"}</p>
    </div>
  );
}

useNonceAccount

Fetch nonce account data:

function NonceInfo({ address }: { address: string }) {
  const { data, isLoading, error } = useNonceAccount(address);

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Error loading nonce</p>;

  return (
    <div>
      <p>Nonce: {data?.blockhash}</p>
      <p>Authority: {data?.authority}</p>
    </div>
  );
}

Transfer Hooks

useSolTransfer

Send SOL to an address:

function SendSol({ destination }: { destination: string }) {
  const { send, isSending, status, signature, error } = useSolTransfer();

  return (
    <div>
      <button
        disabled={isSending}
        onClick={() => send({ destination, amount: 100_000_000n })}
      >
        {isSending ? "Sending..." : "Send 0.1 SOL"}
      </button>
      <p>Status: {status}</p>
      {signature && <p>Signature: {signature}</p>}
      {error && <p>Error: {String(error)}</p>}
    </div>
  );
}

useSplToken

SPL token balance and transfers:

function TokenPanel({ mint, destinationOwner }: { mint: string; destinationOwner: string }) {
  const {
    balance,
    send,
    isSending,
    owner,
    status,
    error,
    sendError,
    sendSignature,
    resetSend,
    refresh,
  } = useSplToken(mint);

  if (status === "disconnected") return <p>Connect wallet</p>;
  if (status === "loading") return <p>Loading...</p>;
  if (status === "error") return <p>Error: {String(error)}</p>;

  return (
    <div>
      <p>Owner: {owner}</p>
      <p>Balance: {balance?.uiAmount ?? "0"}</p>
      <button onClick={refresh}>Refresh</button>
      <button
        disabled={isSending || !owner}
        onClick={() => send({
          amount: 1n,
          destinationOwner,
          amountInBaseUnits: true
        })}
      >
        {isSending ? "Sending..." : "Send 1 token"}
      </button>
      {sendSignature && <p>Signature: {sendSignature}</p>}
      {sendError && (
        <div>
          <p>Error: {String(sendError)}</p>
          <button onClick={resetSend}>Dismiss</button>
        </div>
      )}
    </div>
  );
}

Options:

OptionDescription
commitmentRPC commitment level
ownerOverride balance owner (defaults to connected wallet)
revalidateOnFocusRefresh when window regains focus
swrAdditional SWR options

useWrapSol

Wrap and unwrap SOL to wSOL:

function WrapSolPanel() {
  const { wrap, unwrap, isWrapping, isUnwrapping } = useWrapSol();

  return (
    <div>
      <button
        disabled={isWrapping}
        onClick={() => wrap({ amount: 100_000_000n })}
      >
        Wrap 0.1 SOL
      </button>
      <button
        disabled={isUnwrapping}
        onClick={() => unwrap({ amount: 100_000_000n })}
      >
        Unwrap 0.1 wSOL
      </button>
    </div>
  );
}

Transaction Hooks

useSendTransaction

Simple transaction sending:

function SendPrepared({ instructions }) {
  const { send, isSending, status, signature, error, reset } = useSendTransaction();

  return (
    <div>
      <button
        disabled={isSending}
        onClick={() => send({ instructions })}
      >
        {isSending ? "Submitting..." : "Send Transaction"}
      </button>
      <p>Status: {status}</p>
      {signature && <p>Signature: {signature}</p>}
      {error && <p>Error: {String(error)}</p>}
    </div>
  );
}

useTransactionPool

Build and manage complex transactions:

import type { TransactionInstructionInput } from "@solana/client";

function TransactionBuilder({ ix }: { ix: TransactionInstructionInput }) {
  const session = useWalletSession();
  const {
    addInstruction,
    instructions,
    clearInstructions,
    prepareAndSend,
    isSending,
    sendSignature,
    sendError,
    latestBlockhash,
    reset,
  } = useTransactionPool();

  return (
    <div>
      <button onClick={() => addInstruction(ix)}>
        Add Instruction ({instructions.length})
      </button>
      <button onClick={clearInstructions}>Clear</button>
      <button
        disabled={isSending || !session}
        onClick={() => prepareAndSend({ authority: session })}
      >
        {isSending ? "Sending..." : "Send Transaction"}
      </button>
      <p>Blockhash: {latestBlockhash.blockhash ?? "loading..."}</p>
      {sendSignature && <p>Signature: {sendSignature}</p>}
      {sendError && <p>Error: {String(sendError)}</p>}
    </div>
  );
}

Available methods:

MethodDescription
addInstruction(ix)Add instruction to queue
addInstructions(ixs)Add multiple instructions
removeInstruction(index)Remove instruction at index
clearInstructions()Clear all instructions
replaceInstructions(ixs)Replace instruction queue
prepare(opts)Prepare transaction without sending
prepareAndSend(opts)Prepare and send in one call
send(opts)Send prepared transaction
sign(opts)Sign without sending
reset()Reset all state

Signature Hooks

useSignatureStatus

Watch signature confirmation:

function SignatureInfo({ signature }: { signature: string }) {
  const { status, confirmations, error } = useSignatureStatus(signature);

  return (
    <div>
      <p>Status: {status}</p>
      <p>Confirmations: {confirmations}</p>
      {error && <p>Error: {String(error)}</p>}
    </div>
  );
}

useWaitForSignature

Wait for specific confirmation level:

function SignatureWatcher({ signature }: { signature: string }) {
  const wait = useWaitForSignature(signature, { commitment: "finalized" });

  if (wait.waitStatus === "error") return <p>Failed</p>;
  if (wait.waitStatus === "success") return <p>Finalized</p>;
  if (wait.waitStatus === "waiting") return <p>Waiting...</p>;
  return <p>Provide a signature</p>;
}

Query Hooks

useProgramAccounts

Query accounts owned by a program:

import { SolanaQueryProvider, useProgramAccounts } from "@solana/react-hooks";

function ProgramAccounts({ program }: { program: string }) {
  const { accounts, isLoading, isError, refresh } = useProgramAccounts(program);

  if (isLoading) return <p>Loading...</p>;
  if (isError) return <p>Error</p>;

  return (
    <div>
      <button onClick={refresh}>Refresh</button>
      <ul>
        {accounts.map(({ pubkey }) => (
          <li key={pubkey.toString()}>{pubkey.toString()}</li>
        ))}
      </ul>
    </div>
  );
}

// Wrap with SolanaQueryProvider
function ProgramAccountsSection({ program }: { program: string }) {
  return (
    <SolanaQueryProvider>
      <ProgramAccounts program={program} />
    </SolanaQueryProvider>
  );
}

useSimulateTransaction

Simulate a transaction:

function Simulation({ wire }: { wire: string }) {
  const { logs, isLoading, isError, refresh } = useSimulateTransaction(wire);

  if (isLoading) return <p>Simulating...</p>;
  if (isError) return <p>Simulation failed</p>;

  return (
    <div>
      <button onClick={refresh}>Re-run</button>
      <pre>{JSON.stringify(logs, null, 2)}</pre>
    </div>
  );
}

useLatestBlockhash

Get the latest blockhash:

function BlockhashInfo() {
  const { blockhash, lastValidBlockHeight, isLoading } = useLatestBlockhash();

  if (isLoading) return <p>Loading...</p>;

  return (
    <div>
      <p>Blockhash: {blockhash}</p>
      <p>Valid until: {lastValidBlockHeight}</p>
    </div>
  );
}

Store Hook

useClientStore

Access the underlying Zustand store:

function ClusterBadge() {
  const cluster = useClientStore((s) => s.cluster);
  return <p>Endpoint: {cluster.endpoint}</p>;
}

function WalletStatus() {
  const walletStatus = useClientStore((s) => s.wallet.status);
  return <p>Status: {walletStatus}</p>;
}

Suspense Support

Enable Suspense per subtree:

import { SolanaQueryProvider, useBalance } from "@solana/react-hooks";
import { Suspense } from "react";

function BalanceDetails({ address }: { address: string }) {
  const balance = useBalance(address);
  return <p>Lamports: {balance.lamports?.toString() ?? "0"}</p>;
}

export function WalletPanel({ address }: { address: string }) {
  return (
    <SolanaQueryProvider suspense>
      <Suspense fallback={<p>Loading balance...</p>}>
        <BalanceDetails address={address} />
      </Suspense>
    </SolanaQueryProvider>
  );
}

SWR Configuration

Configure caching and revalidation:

export function App() {
  return (
    <SolanaProvider
      client={client}
      query={{
        config: {
          revalidateOnFocus: false,
          revalidateOnReconnect: false,
          refreshInterval: 30_000,
          dedupingInterval: 2000,
        },
      }}
    >
      <YourApp />
    </SolanaProvider>
  );
}

Default SWR settings:

OptionDefault
revalidateOnFocustrue
revalidateOnReconnecttrue
revalidateIfStaletrue
dedupingInterval2000ms
focusThrottleInterval5000ms

Hooks Reference

Wallet Hooks

HookDescription
useWalletConnectionComplete wallet connection management
useWalletAccess wallet state
useWalletSessionGet current session for signing
useWalletActionsConnect/disconnect actions
useWalletModalStateModal visibility management
useConnectWalletConnect action only
useDisconnectWalletDisconnect action only

Data Hooks

HookDescription
useBalanceFetch and watch balance
useAccountFetch and watch account
useLookupTableFetch lookup table
useNonceAccountFetch nonce account
useProgramAccountsQuery program accounts

Transaction Hooks

HookDescription
useSolTransferSend SOL
useSplTokenSPL token operations
useWrapSolWrap/unwrap SOL
useSendTransactionSimple transaction send
useTransactionPoolComplex transaction building
useSimulateTransactionSimulate transactions

Status Hooks

HookDescription
useSignatureStatusWatch signature status
useWaitForSignatureWait for confirmation
useClusterStateCurrent cluster info
useClusterStatusCluster connection status
useLatestBlockhashGet latest blockhash

Utility Hooks

HookDescription
useClientStoreAccess Zustand store
useSolanaClientAccess client instance

On this page