iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

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…