Sitemap

Wagmi v2 & Viem Hooks Guide for React Applications

5 min readAug 6, 2025

--

Press enter or click to view image in full size

Published by mirtalha

Installation

First, install the required packages:

npm install wagmi viem @tanstack/react-query
# or
yarn add wagmi viem @tanstack/react-query

Basic Setup

1. Configuration Setup

// config/wagmi.js
import { createConfig, http } from 'wagmi'
import { mainnet, sepolia } from 'wagmi/chains'
import { injected, metaMask, safe, walletConnect } from 'wagmi/connectors'

const projectId = 'YOUR_WALLETCONNECT_PROJECT_ID'

export const config = createConfig({
chains: [mainnet, sepolia],
connectors: [
injected(),
walletConnect({ projectId }),
metaMask(),
safe(),
],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})

2. App Provider Setup

// App.js
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { WagmiProvider } from 'wagmi'
import { config } from './config/wagmi'

const queryClient = new QueryClient()

function App() {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<YourAppComponents />
</QueryClientProvider>
</WagmiProvider>
)
}

Core Wagmi v2 Hooks Connection Hooks

useConnect

import { useConnect } from 'wagmi'

function ConnectWallet() {
const { connectors, connect, status, error } = useConnect()

return (
<div>
{connectors.map((connector) => (
<button
key={connector.uid}
onClick={() => connect({ connector })}
disabled={status === 'pending'}
>
{connector.name}
</button>
))}
{error && <div>Error: {error.message}</div>}
</div>
)
}

useDisconnect

import { useDisconnect } from 'wagmi'

function DisconnectButton() {
const { disconnect } = useDisconnect()

return <button onClick={() => disconnect()}>Disconnect</button>
}

useAccount

import { useAccount } from 'wagmi'

function AccountInfo() {
const { address, isConnected, isConnecting, isDisconnected } = useAccount()

if (isConnecting) return <div>Connecting...</div>
if (isDisconnected) return <div>Disconnected</div>

return (
<div>
<div>Address: {address}</div>
<div>Connected: {isConnected}</div>
</div>
)
}

Balance and Chain Hooks

useBalance

import { useBalance } from 'wagmi'

function Balance() {
const { data: balance, isError, isLoading } = useBalance({
address: '0x...',
})

if (isLoading) return <div>Fetching balance…</div>
if (isError) return <div>Error fetching balance</div>

return (
<div>
Balance: {balance?.formatted} {balance?.symbol}
</div>
)
}

useChainId

import { useChainId } from 'wagmi'

function ChainInfo() {
const chainId = useChainId()
return <div>Chain ID: {chainId}</div>
}

useSwitchChain

import { useSwitchChain } from 'wagmi'

function SwitchChain() {
const { chains, switchChain } = useSwitchChain()

return (
<div>
{chains.map((chain) => (
<button key={chain.id} onClick={() => switchChain({ chainId: chain.id })}>
Switch to {chain.name}
</button>
))}
</div>
)
}

Transaction Hooks

useSendTransaction

import { useSendTransaction } from 'wagmi'
import { parseEther } from 'viem'

function SendTransaction() {
const { data: hash, sendTransaction } = useSendTransaction()

const handleSend = () => {
sendTransaction({
to: '0x...',
value: parseEther('0.01'),
})
}

return (
<div>
<button onClick={handleSend}>Send Transaction</button>
{hash && <div>Transaction Hash: {hash}</div>}
</div>
)
}

useWaitForTransactionReceipt

import { useWaitForTransactionReceipt } from 'wagmi'

function TransactionStatus({ hash }) {
const { data: receipt, isLoading, isSuccess } = useWaitForTransactionReceipt({
hash,
})

return (
<div>
{isLoading && <div>Waiting for confirmation...</div>}
{isSuccess && <div>Transaction confirmed!</div>}
{receipt && <div>Block: {receipt.blockNumber}</div>}
</div>
)
}

Contract Interaction Hooks

useReadContract

import { useReadContract } from 'wagmi'

const contractABI = [
{
name: 'balanceOf',
type: 'function',
stateMutability: 'view',
inputs: [{ name: 'account', type: 'address' }],
outputs: [{ name: '', type: 'uint256' }],
},
]

function TokenBalance() {
const { data: balance } = useReadContract({
address: '0x...', // Token contract address
abi: contractABI,
functionName: 'balanceOf',
args: ['0x...'], // User address
})

return <div>Token Balance: {balance?.toString()}</div>
}

useWriteContract

import { useWriteContract } from 'wagmi'

function WriteContract() {
const { data: hash, writeContract } = useWriteContract()

const handleWrite = () => {
writeContract({
address: '0x...',
abi: contractABI,
functionName: 'transfer',
args: ['0x...', 1000n],
})
}

return (
<div>
<button onClick={handleWrite}>Transfer Tokens</button>
{hash && <div>Transaction: {hash}</div>}
</div>
)
}

useSimulateContract

import { useSimulateContract, useWriteContract } from 'wagmi'

function SafeContractWrite() {
const { data: simulateData } = useSimulateContract({
address: '0x...',
abi: contractABI,
functionName: 'transfer',
args: ['0x...', 1000n],
})

const { writeContract } = useWriteContract()

const handleWrite = () => {
writeContract(simulateData!.request)
}

return (
<button
onClick={handleWrite}
disabled={!simulateData}
>
Safe Transfer
</button>
)
}

Event Hooks

useWatchContractEvent

import { useWatchContractEvent } from 'wagmi'

function EventWatcher() {
useWatchContractEvent({
address: '0x...',
abi: contractABI,
eventName: 'Transfer',
onLogs(logs) {
console.log('New transfer events!', logs)
},
})

return <div>Watching for Transfer events...</div>
}

Advanced Patterns

Custom Hook for Token Operations

import { useReadContract, useWriteContract, useSimulateContract } from 'wagmi'

function useToken(tokenAddress) {
const { data: name } = useReadContract({
address: tokenAddress,
abi: erc20ABI,
functionName: 'name',
})

const { data: symbol } = useReadContract({
address: tokenAddress,
abi: erc20ABI,
functionName: 'symbol',
})

const { writeContract: transfer } = useWriteContract()

const transferToken = (to, amount) => {
transfer({
address: tokenAddress,
abi: erc20ABI,
functionName: 'transfer',
args: [to, amount],
})
}

return {
name,
symbol,
transferToken,
}
}

Get mirbasit01’s stories in your inbox

Join Medium for free to get updates from this writer.

Error Handling Pattern

import { useAccount, useConnect } from 'wagmi'

function WalletConnection() {
const { address, isConnected } = useAccount()
const { connect, connectors, error, isLoading } = useConnect()

if (error) {
return (
<div className="error">
Failed to connect: {error.message}
</div>
)
}

if (isConnected) {
return <div>Connected to {address}</div>
}

return (
<div>
{connectors.map((connector) => (
<button
key={connector.uid}
onClick={() => connect({ connector })}
disabled={isLoading}
>
{isLoading ? 'Connecting...' : `Connect ${connector.name}`}
</button>
))}
</div>
)
}

Complete Example Component

import { useState } from 'react'
import {
useAccount,
useConnect,
useDisconnect,
useBalance,
useSendTransaction,
useWaitForTransactionReceipt
} from 'wagmi'
import { parseEther, formatEther } from 'viem'

function WalletApp() {
const [recipient, setRecipient] = useState('')
const [amount, setAmount] = useState('')

const { address, isConnected } = useAccount()
const { connectors, connect } = useConnect()
const { disconnect } = useDisconnect()
const { data: balance } = useBalance({ address })
const { data: hash, sendTransaction } = useSendTransaction()
const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash })

const handleSend = (e) => {
e.preventDefault()
sendTransaction({
to: recipient,
value: parseEther(amount),
})
}

if (!isConnected) {
return (
<div>
<h2>Connect Wallet</h2>
{connectors.map((connector) => (
<button
key={connector.uid}
onClick={() => connect({ connector })}
>
Connect {connector.name}
</button>
))}
</div>
)
}

return (
<div>
<div>
<h2>Wallet Connected</h2>
<p>Address: {address}</p>
<p>Balance: {balance ? formatEther(balance.value) : '0'} ETH</p>
<button onClick={() => disconnect()}>Disconnect</button>
</div>

<form onSubmit={handleSend}>
<h3>Send Transaction</h3>
<input
type="text"
placeholder="Recipient address"
value={recipient}
onChange={(e) => setRecipient(e.target.value)}
/>
<input
type="text"
placeholder="Amount in ETH"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
<button type="submit" disabled={isConfirming}>
{isConfirming ? 'Confirming...' : 'Send'}
</button>
</form>

{hash && <div>Transaction Hash: {hash}</div>}
{isConfirming && <div>Waiting for confirmation...</div>}
{isSuccess && <div>Transaction confirmed!</div>}
</div>
)
}

Key Changes in Wagmi v2

  • New Import Structure: Import directly from ‘wagmi’ instead of ‘wagmi/actions’
  • Simplified Configuration: Use createConfig instead of configureChains
  • Better TypeScript Support: Enhanced type inference
  • Improved Performance: Better caching and optimization
  • Viem Integration: Direct viem utilities usage (parseEther, formatEther, etc.)

Best Practices

  1. Always handle loading and error states
  2. Use simulation before writing to contracts
  3. Implement proper error boundaries
  4. Cache contract ABIs appropriately
  5. Use TypeScript for better development experience
  6. Test with different wallet connectors
  7. Handle network switching gracefully

Main Features Covered:

  • Complete setup with providers and configuration
  • All essential hooks for wallet connection, balance checking, transactions
  • Contract interaction hooks (read, write, simulate)
  • Event watching and transaction monitoring
  • Error handling patterns

Key wagmi v2 Hooks:

  • useConnect, useDisconnect, useAccount - wallet management
  • useBalance, useChainId, useSwitchChain - blockchain data
  • useSendTransaction, useWaitForTransactionReceipt - transactions
  • useReadContract, useWriteContract, useSimulateContract - smart contracts
  • useWatchContractEvent - event monitoring

Important Changes in v2:

  • Simplified configuration with createConfig
  • Direct integration with viem utilities
  • Better TypeScript support
  • Improved performance and caching
  • Performance Optimization Tips — Shows how to optimize re-renders and batch contract reads
  • Real-World Production Patterns — Multi-chain support and transaction queue management
  • Testing Strategies — Mock setup and integration testing examples
  • Security Best Practices — Input validation and rate limiting
  • Migration Guide — Clear v1 to v2 migration examples
  • Common Pitfalls — What to avoid and best practices
  • Resources & Next Steps — Useful links and community resources

--

--

mirbasit01
mirbasit01

Written by mirbasit01

Front-End Developer | React.js | Next.js | TypeScript | Tailwind CSS | Web3.js | Smart Contracts | Creating responsive & innovative web apps