wagmi
wagmi is the React hooks library for Ethereum: useAccount, useReadContract, useWriteContract, useWatchContractEvent, plus connector hooks for MetaMask, WalletConnect, Coinbase Wallet, and Frame. It is built on top of viem, so the type-safety story is end-to-end and the bundle is small enough for production dapps.
wagmi setup, connect, read, write, watch
EXAMPLE
// npm i wagmi viem @tanstack/react-query
// src/wagmi.ts
import { createConfig, http } from 'wagmi';
import { mainnet, sepolia } from 'wagmi/chains';
import { injected, walletConnect, coinbaseWallet } from 'wagmi/connectors';
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http('https://eth-mainnet.example/rpc'),
[sepolia.id]: http('https://eth-sepolia.example/rpc'),
},
connectors: [
injected(),
walletConnect({ projectId: process.env.NEXT_PUBLIC_WC_ID! }),
coinbaseWallet({ appName: 'Shop' }),
],
});
// src/_app.tsx — provider at the root
import { WagmiProvider } from 'wagmi';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { config } from '../wagmi';
const queryClient = new QueryClient();
export default function App({ children }: { children: React.ReactNode }) {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</WagmiProvider>
);
}
// src/components/ConnectButton.tsx
import { useAccount, useConnect, useDisconnect, useEnsName } from 'wagmi';
export function ConnectButton() {
const { address, isConnected, chain } = useAccount();
const { connectors, connect, status, error } = useConnect();
const { disconnect } = useDisconnect();
const { data: ens } = useEnsName({ address });
if (isConnected) {
return (
<div>
<div>Hi, {ens ?? address?.slice(0, 6) + '...' + address?.slice(-4)}</div>
<div>Chain: {chain?.name}</div>
<button onClick={() => disconnect()}>Disconnect</button>
</div>
);
}
return (
<div>
{connectors.map((c) => (
<button key={c.uid} onClick={() => connect({ connector: c })}>{c.name}</button>
))}
{error && <p>{error.message}</p>}
</div>
);
}
// src/components/UsdcBalance.tsx — typed read
import { useAccount, useReadContract } from 'wagmi';
import { erc20Abi, formatUnits } from 'viem';
export function UsdcBalance() {
const { address } = useAccount();
const { data, isLoading, error, refetch } = useReadContract({
address: '0xA0b86991c6218B36c1d19D4a2e9Eb0cE3606eB48',
abi: erc20Abi,
functionName: 'balanceOf',
args: address ? [address] : undefined,
query: { enabled: !!address, refetchInterval: 10_000 },
});
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return <p>USDC: {data != null ? formatUnits(data, 6) : '-'}</p>;
}
// src/components/TransferButton.tsx — typed write + status
import { useWriteContract, useWaitForTransactionReceipt } from 'wagmi';
import { erc20Abi, parseUnits } from 'viem';
export function TransferButton() {
const { writeContract, data: hash, isPending, error } = useWriteContract();
const { isLoading: confirming, isSuccess } = useWaitForTransactionReceipt({ hash });
const onClick = () => writeContract({
address: '0xA0b86991c6218B36c1d19D4a2e9Eb0cE3606eB48',
abi: erc20Abi,
functionName: 'transfer',
args: ['0x000000000000000000000000000000000000dEaD', parseUnits('1', 6)],
});
return (
<div>
<button onClick={onClick} disabled={isPending || confirming}>
{isPending ? 'Sign...' : confirming ? 'Mining...' : 'Burn 1 USDC'}
</button>
{error && <p>Error: {error.message}</p>}
{isSuccess && <p>Done. Tx: {hash}</p>}
</div>
);
}
// src/components/LiveTransfers.tsx — watch events
import { useAccount, useWatchContractEvent } from 'wagmi';
import { erc20Abi } from 'viem';
export function LiveTransfers() {
const { address } = useAccount();
useWatchContractEvent({
address: '0xA0b86991c6218B36c1d19D4a2e9Eb0cE3606eB48',
abi: erc20Abi,
eventName: 'Transfer',
args: address ? { from: address } : undefined,
onLogs(logs) {
for (const log of logs) {
console.log('I sent', log.args.value, 'to', log.args.to);
}
},
});
return null;
}
Why it matters
Wrap wagmi in @tanstack/react-query (the QueryClientProvider) — wagmi pipes every read through it. You get caching, deduplication, refetch-on-focus, and devtools for free, and you can pre-load critical data on the server during SSR. Skipping the provider works but throws away half of wagmis polish.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// React hooks for wallets + contracts
import { useReadContract } from 'wagmi';
const { data } = useReadContract({
address: '0x…',
abi,
functionName: 'balanceOf',
args: [account],
});
Try it Yourself »
Discussion
Loading…