Wagmi Frontend Integration
Wagmi v2 is the de-facto standard for React + EVM. Migration from Wagmi v1 to v2 broke many APIs (transition to Viem instead of ethers.js, hooks changes), so if the project is written for the old version — integration includes migration. If from scratch — use v2 right away.
Setup and Configuration
// config.ts
import { createConfig, http } from 'wagmi';
import { mainnet, polygon, arbitrum, base } from 'wagmi/chains';
import { injected, coinbaseWallet, walletConnect } from 'wagmi/connectors';
export const config = createConfig({
chains: [mainnet, polygon, arbitrum, base],
transports: {
[mainnet.id]: http('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY'),
[polygon.id]: http('https://polygon-mainnet.g.alchemy.com/v2/YOUR_KEY'),
[arbitrum.id]: http('https://arb-mainnet.g.alchemy.com/v2/YOUR_KEY'),
[base.id]: http('https://base-mainnet.g.alchemy.com/v2/YOUR_KEY'),
},
connectors: [
injected(),
coinbaseWallet({ appName: 'AppName' }),
walletConnect({ projectId: process.env.VITE_WC_PROJECT_ID! }),
],
});
WagmiProvider wraps the application; QueryClientProvider (TanStack Query) is mandatory, Wagmi uses it for caching.
Main Patterns
Reading Data
useReadContract for a single call, useReadContracts for a batch via Multicall3:
const { data: balance } = useReadContract({
address: TOKEN_ADDRESS,
abi: erc20Abi,
functionName: 'balanceOf',
args: [address],
query: { enabled: !!address },
});
query.enabled is a critical option: without it the hook tries to read before address is determined. staleTime and gcTime control how often data is re-read — reasonable for balances is 30 seconds, for slow-changing contract parameters — 5 minutes.
Writing (Transactions)
const { writeContractAsync } = useWriteContract();
const { isLoading: isConfirming } = useWaitForTransactionReceipt({ hash });
const handleStake = async () => {
const hash = await writeContractAsync({
address: STAKING_ADDRESS,
abi: stakingAbi,
functionName: 'stake',
args: [parseEther(amount)],
});
// hash received — transaction sent, waiting for confirmation
};
Signatures
For SIWE and permit signatures — useSignMessage and useSignTypedData:
const { signTypedDataAsync } = useSignTypedData();
// EIP-712 typed data for permit
const signature = await signTypedDataAsync({
domain, types, primaryType: 'Permit', message: permitMessage,
});
Typical Integration Problems
SSR/hydration: Next.js App Router + Wagmi require 'use client' on components with hooks and careful server/client logic separation. useAccount() on server always returns disconnected — this is normal.
ABI typing: Wagmi + Viem generate types from ABI via as const. Without as const, type inference for arguments and return values is lost.
ENS resolution: useEnsName and useEnsAddress only work on mainnet. For other networks — explicitly pass chainId: mainnet.id.
Stale data after transaction: after successful transaction, invalidate the cache:
const queryClient = useQueryClient();
// after successful writeContract:
queryClient.invalidateQueries({ queryKey: ['readContract', ...] });
Or use useWaitForTransactionReceipt with onSuccess callback for automatic invalidation.
Timeline Guidelines
Setup from scratch (multichain, wallet UI, basic read/write hooks): 1 day. Integrating existing React application with multiple smart contracts and migrating from v1: 2-3 days.







