@solana/client
Framework-agnostic building blocks for Solana RPC, subscriptions, wallets, and transactions. Works
in any runtime (React, Svelte, API routes, workers, etc.).
Status: Experimental – expect rapid iteration.
Install
npm install @solana/client
Quickstart
- Choose Wallet Standard connectors (auto-discovery is the fastest way to start).
- Create a Solana client.
- Call actions, watchers, and helpers anywhere in your app (React, APIs, workers, etc.).
import { autoDiscover, createClient } from "@solana/client";
const client = createClient({
endpoint: "https://api.devnet.solana.com",
websocketEndpoint: "wss://api.devnet.solana.com",
walletConnectors: autoDiscover(),
});
await client.actions.connectWallet("phantom");
const wallet = client.store.getState().wallet;
if (wallet.status === "connected") {
const account = await client.actions.fetchAccount(wallet.session.account.address);
console.log(account.lamports?.toString());
}
Common Solana flows (copy/paste)
Connect, disconnect, and inspect wallet state
const connectors = client.connectors.all;
await client.actions.connectWallet(connectors[0].id);
const wallet = client.store.getState().wallet;
if (wallet.status === "connected") {
console.log(wallet.session.account.address.toString());
}
await client.actions.disconnectWallet();
Fetch and watch lamports
import { toAddress } from "@solana/client";
const address = toAddress("Fg6PaFpoGXkYsidMpWFKfwtz6DhFVyG4dL1x8kj7ZJup");
const lamports = await client.actions.fetchBalance(address);
console.log(`Lamports: ${lamports.toString()}`);
const watcher = client.watchers.watchBalance({ address }, (nextLamports) => {
console.log("Updated balance:", nextLamports.toString());
});
watcher.abort();
Request an airdrop (devnet/testnet)
const signature = await client.actions.requestAirdrop(address, 1_000_000_000n);
console.log(signature.toString());
Send SOL
const wallet = client.store.getState().wallet;
if (wallet.status !== "connected") throw new Error("Connect wallet first");
const signature = await client.solTransfer.sendTransfer({
amount: 100_000_000n,
authority: wallet.session,
destination: "Ff34MXWdgNsEJ1kJFj9cXmrEe7y2P93b95mGu5CJjBQJ",
});
console.log(signature.toString());
SPL token balance + transfer
const wallet = client.store.getState().wallet;
if (wallet.status !== "connected") throw new Error("Connect wallet first");
const usdc = client.splToken({ mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" });
const balance = await usdc.fetchBalance(wallet.session.account.address);
console.log(`Balance: ${balance.uiAmount}`);
const signature = await usdc.sendTransfer({
amount: 1n,
authority: wallet.session,
destinationOwner: "Ff34MXWdgNsEJ1kJFj9cXmrEe7y2P93b95mGu5CJjBQJ",
});
console.log(signature.toString());
Fetch address lookup tables
import { toAddress } from "@solana/client";
const lut = await client.actions.fetchLookupTable(
toAddress("AddressLookupTab1e1111111111111111111111111"),
);
console.log(`Addresses in LUT: ${lut.addresses.length}`);
const luts = await client.actions.fetchLookupTables([lutAddress1, lutAddress2]);
Fetch nonce accounts
const nonce = await client.actions.fetchNonceAccount(
toAddress("NonceAccountAddress111111111111111111111111"),
);
console.log(`Nonce: ${nonce.blockhash}`);
console.log(`Authority: ${nonce.authority}`);
Build and send arbitrary transactions
import { getTransferSolInstruction } from "@solana-program/system";
const wallet = client.store.getState().wallet;
if (wallet.status !== "connected") throw new Error("Connect wallet first");
const prepared = await client.transaction.prepare({
authority: wallet.session,
instructions: [
getTransferSolInstruction({
destination: "Ff34MXWdgNsEJ1kJFj9cXmrEe7y2P93b95mGu5CJjBQJ",
lamports: 10_000n,
source: wallet.session.account.address,
}),
],
version: "auto",
});
const wire = await client.transaction.toWire(prepared);
const signature = await client.transaction.send(prepared);
console.log(signature.toString());
Watch signature confirmations
const watcher = client.watchers.watchSignature(
{ signature, commitment: "confirmed" },
(notification) => console.log("Signature update:", notification),
);
watcher.abort();
Cluster monikers and endpoints
Pass a cluster moniker to auto-resolve RPC + WebSocket URLs. Monikers map to:
mainnet / mainnet-beta → https://api.mainnet-beta.solana.com
testnet → https://api.testnet.solana.com
devnet (default) → https://api.devnet.solana.com
localnet / localhost → http://127.0.0.1:8899
WebSocket URLs are inferred (wss:// or ws://) when not supplied. Override with endpoint/rpc or websocket/websocketEndpoint when you need a custom host.
import { autoDiscover, createClient } from "@solana/client";
const client = createClient({
cluster: "mainnet",
walletConnectors: autoDiscover(),
});
Custom endpoint with inferred WebSocket:
const client = createClient({
endpoint: "http://127.0.0.1:8899",
});
Use resolveCluster directly when you need the resolved URLs without creating a client:
import { resolveCluster } from "@solana/client";
const resolved = resolveCluster({ moniker: "testnet" });
console.log(resolved.endpoint, resolved.websocketEndpoint);
Notes:
- Default moniker is
devnet when nothing is provided; moniker becomes custom when you pass a raw endpoint.
createClient, createDefaultClient (resolveClientConfig), and SolanaProvider all use resolveCluster under the hood, so the moniker/endpoint behavior is consistent across entrypoints.
Notes and defaults
- Wallet connectors:
autoDiscover() picks up Wallet Standard injectables; compose phantom(), solflare(), backpack(), or injected() when you need explicit control.
- Store: built on Zustand; pass
createStore to createClient for custom persistence or server-side stores. serializeSolanaState / deserializeSolanaState help save and restore cluster + wallet metadata.
- Actions:
fetchAccount, fetchBalance, fetchLookupTable, fetchLookupTables, fetchNonceAccount, setCluster, requestAirdrop, sendTransaction, and wallet connect/disconnect keep the store in sync.
- Watchers:
watchAccount, watchBalance, and watchSignature stream updates into the store and return an abort() handle for cleanup.
- Helpers:
solTransfer, splToken, and transaction cover common transfers plus low-level prepare/sign/toWire flows. Transaction versions default to 0 when any instruction references address lookup tables, otherwise legacy; override with version when needed.
Scripts
pnpm build – compile JS and type definitions
pnpm test:typecheck – strict type-checking
pnpm lint / pnpm format – Biome-powered linting and formatting
More resources
- Playground:
examples/vite-react (run with pnpm install && pnpm dev).
- Next.js reference app:
examples/nextjs.
- Client APIs live in
src/actions.ts, src/watchers, and src/features/* for helper internals.