butr
Guides

Read balances

useBalance for EVM wallets, or query your own RPC for every other platform and for full control.

useBalance: the built-in path

useBalance(wallet, options) takes a connected wallet, from useWallet() or useSelectedWallet(platform), and returns a typed lifecycle state plus refetch:

import { useBalance, useSelectedWallet } from "@usebutr/react";

const wallet = useSelectedWallet("evm");
const balance = useBalance(wallet);

const text =
  balance.status === "success"
    ? `${balance.data.formatted} ${balance.data.symbol}`
    : balance.status === "loading"
      ? "…"
      : balance.status === "error"
        ? balance.error.message
        : "—";

With no wallet (undefined) it stays "idle". Both options are optional:

OptionDefaultMeaning
accountthe wallet's active accountwhich exposed account to read
tokenthe native assetan ERC-20 contract address
const usdc = useBalance(wallet, { token: USDC_ADDRESS });

balance.data is a Balance:

type Balance = {
  decimals: number; // 18 for ETH, or the token's decimals
  formatted: string; // human-readable, trailing zeros trimmed
  symbol: string; // "ETH", or the token's symbol
  value: bigint; // raw integer amount
};

refetch() reads again, for example after a transaction lands. The hook also refetches when the wallet, account, or token changes.

Only EVM adapters define getBalance: they read through the wallet's own provider (eth_getBalance, or balanceOf for a token). butr ships no RPC for Solana, Sui, Bitcoin, or Polkadot, and a Ledger has none either, so for those wallets useBalance stays "idle". Query your own RPC for them. The native balance is labelled "ETH" on every EVM chain.

To call it without the hook, check for the method first:

if (wallet.connector.getBalance) {
  const balance = await wallet.connector.getBalance({ account: wallet.account, token: USDC });
}

Querying RPC yourself

For every other platform, and for full control on EVM (custom RPC, caching, token lists), read through your chain library. butr stays out of the way:

// viem: its own public client, not the wallet's RPC
const wei = await publicClient.getBalance({ address: account });
const eth = `${formatEther(wei)} ETH`;

// gill / @solana/kit
const { value } = await rpc.getBalance(addr).send();
const sol = `${Number(value) / 1_000_000_000} SOL`;

For a reactive Solana read that auto-fetches and watches, framework-kit's useBalance works against a butr-managed wallet; see the framework-kit integration.

Source: useBalance usage in apps/demo-vite/src/app.tsx; manual RPC reads in apps/demo-with-viem/src/app.tsx, apps/demo-with-gill/src/app.tsx, and apps/demo-with-solana-kit/src/app.tsx; framework-kit's useBalance in apps/demo-with-solana-framework-kit/src/app.tsx.