Building a React dApp frontend with wagmi comes down to three moving parts: a config that says which chains to talk to and how, a provider that wraps your app, and a set of hooks that connect a wallet, read smart-contract data, and send transactions. wagmi is a React Hooks library for Ethereum and other EVM-compatible chains, built on viem for the low-level work and TanStack Query for caching.
Get those three parts in place and the rest of your interface is ordinary React. This guide goes from what the wagmi library actually is, through why a frontend needs it at all, to a step-by-step build — config, provider, wallet connection, reads, and writes — using the current v3 API. The version matters here: enough changed in v3 that older tutorials and AI-generated snippets will steer you wrong.
What Is It, and What Does It Sit On Top Of?
wagmi is a collection of React hooks for connecting wallets, reading account state, and calling smart contracts on Ethereum and EVM chains (wagmi docs). Instead of writing raw JSON-RPC calls and wiring up your own loading and error state, you call a hook like useReadContract and get back typed data, a status, and automatic refreshes when the chain or account changes.
Two libraries do the work underneath. viem is the TypeScript interface that performs the actual blockchain operations — encoding calls, sending transactions, decoding results. TanStack Query handles caching, request deduplication, and background refetching, so repeated reads of the same value don’t hammer your endpoint. wagmi ties both to React’s render cycle.
That stack is what people mean when they call wagmi reactive. When a user switches wallets or the active chain changes, the hooks re-run and your components update on their own. It’s a JavaScript and TypeScript library, and TypeScript is where it earns its keep — hook return types are inferred directly from a contract’s ABI, the description of a contract’s functions.
Why Not Just Use viem or ethers.js Directly?
You can talk to Ethereum from React with viem or ethers.js alone, but you end up rebuilding the same plumbing every project needs. A frontend has to track whether a wallet is connected, which account and chain are active, cache results so the UI stays fast, refetch after a new block, and clean all of it up when a component unmounts.
wagmi packages that plumbing into hooks. Wallet connection, multi-wallet handling, chain switching, ENS lookups, balance reads, contract calls, and transaction tracking each get a dedicated hook with loading and error states already wired in. The trade-off is a dependency and an opinionated structure. If you only need a single read inside a script with no UI, plain viem is lighter and more direct.
For a React app with real users, though, the plumbing is most of the work, and that’s the part these hooks remove. Reading a value stays easy either way. Keeping the screen in sync with a chain that keeps changing under you is the hard part, and that’s what wagmi is built for.
Who Builds Frontends With It?
Teams shipping almost any React interface that touches on-chain data reach for it.
- DeFi dashboards and swap interfaces that read balances and submit transactions.
- NFT mint pages and marketplaces that call contract functions and track confirmations.
- Wallet UIs and portfolio trackers that watch accounts across several chains at once.
- Governance and DAO tools where users sign messages and vote through contracts.
The wagmi library is widely used — over half a million weekly downloads on npm — and that popularity has a practical payoff. The best-known connect-wallet kits, RainbowKit, ConnectKit, and Reown AppKit (formerly Web3Modal), are all built on top of wagmi. If you later want a polished, prebuilt wallet modal instead of your own buttons, you can drop one in without leaving the ecosystem you already set up.
What Changed in Version 3?
Read this section before you copy any example from elsewhere. wagmi is on v3 as of 2026, and two changes break older code.
First, connectors are no longer bundled. In the maintainers’ words:
“Wagmi v3 gives you total control over connector dependencies.” — Wagmi v3 migration guide
Earlier versions shipped the wallet SDKs inside the package. In v3 you install the ones you use yourself. The injected connector for browser-extension wallets needs nothing extra, while walletConnect, metaMask, and coinbaseWallet each pull in their own SDK as a separate package.
Second, the account hooks were renamed. useAccount is now useConnection, useAccountEffect is useConnectionEffect, and useSwitchAccount is useSwitchConnection. The reasoning is that wagmi tracks connections to wallet providers, not “accounts” in the abstract.
One older pattern is worth flagging too. If a snippet uses WagmiConfig or createClient, it’s v1 — v2 replaced those with WagmiProvider and createConfig, and v3 keeps that shape. Treat anything older as material to rewrite, not copy.
How to Connect a React Frontend to Ethereum, Step by Step
The setup is short: install, write a config, wrap the app, then use hooks. Every step below uses the v3 API.
Step 1 — Install or Scaffold the Project
For a new app, the CLI sets everything up with sensible defaults:
bash
npm create wagmi@latest
To add wagmi to an existing React project by hand, install the three packages together:
bash
npm install wagmi [email protected] @tanstack/react-query
viem and TanStack Query are peers wagmi depends on, so they go in alongside it.
Step 2 — Create the Config
The config names the chains your app supports and how it reaches each one. A transport is that connection — usually http() pointed at an endpoint URL.
ts
import { createConfig, http } from 'wagmi'
import { mainnet, sepolia } from 'wagmi/chains'
import { injected, walletConnect } from 'wagmi/connectors'
export const config = createConfig({
chains: [mainnet, sepolia],
connectors: [
injected(),
walletConnect({ projectId: 'YOUR_PROJECT_ID' }),
],
transports: {
[mainnet.id]: http(import.meta.env.VITE_ETH_RPC_URL),
[sepolia.id]: http(import.meta.env.VITE_SEPOLIA_RPC_URL),
},
})
Calling http() with no argument falls back to a chain’s default public endpoint. That’s fine for a first run but shared and rate-limited. For anything past a demo, pass your own Ethereum endpoint URL and keep the key in an environment variable instead of in the file.
That endpoint is the piece we handle at NOWNodes: an Ethereum endpoint over HTTP and WebSocket behind a single API key, so your transport has a stable URL to point at without you syncing client software. The same key covers Sepolia, so the config above can test on the testnet and move to mainnet by swapping the URL. The hooks here are the frontend half of a dApp; the contract side of the build covers the rest.
Step 3 — Wrap Your App
Two providers go at the root: WagmiProvider with your config, and QueryClientProvider inside it. The second is not optional. wagmi delegates caching to TanStack Query, and a hook used outside the query provider will throw.
tsx
import { WagmiProvider } from 'wagmi'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { config } from './config'
const queryClient = new QueryClient()
export function App() {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
{/* your components */}
</QueryClientProvider>
</WagmiProvider>
)
}
Step 4 — Connect a Wallet
useConnect exposes the connectors from your config plus a connect function, useConnection reports the active address, and useDisconnect ends the session.
tsx
import { useConnect, useConnection, useDisconnect } from 'wagmi'
export function WalletButton() {
const { address, isConnected } = useConnection()
const { connectors, connect } = useConnect()
const { disconnect } = useDisconnect()
if (isConnected)
return <button onClick={() => disconnect()}>Disconnect {address}</button>
return connectors.map((connector) => (
<button key={connector.uid} onClick={() => connect({ connector })}>
{connector.name}
</button>
))
}
useConnection is the v3 name for what older code calls useAccount, so a snippet using the old name is the first thing to update.
Step 5 — Read Contract Data
Reads use useReadContract. Give it the contract address, the relevant piece of the ABI, the function name, and any arguments.
tsx
import { useReadContract } from 'wagmi'
import { erc20Abi } from 'viem'
const { data: balance, isPending } = useReadContract({
address: '0xToken...',
abi: erc20Abi,
functionName: 'balanceOf',
args: ['0xUser...'],
})
The hook returns typed data, refreshes on its own, and shares the TanStack Query cache, so two components asking for the same value trigger a single request. For a plain ETH balance, useBalance({ address }) is simpler. One thing to expect: token amounts come back as bigint, not regular numbers, so format them before display.
Step 6 — Send a Transaction
Writes take two hooks — useWriteContract to submit, and useWaitForTransactionReceipt to watch for confirmation.
tsx
import { useWriteContract, useWaitForTransactionReceipt } from 'wagmi'
const { data: hash, writeContract, isPending } = useWriteContract()
const { isLoading: confirming, isSuccess } = useWaitForTransactionReceipt({ hash })
// inside a click handler:
writeContract({
address: '0xContract...',
abi: myAbi,
functionName: 'mint',
args: [1n],
})
The user’s wallet signs and pays the gas; your app only submits and tracks the result. For a write that can revert, run useSimulateContract first to catch the failure before the user ever signs.
Which Hook Does Each Job?
| Task | Hook |
|---|---|
| Connect or disconnect a wallet | useConnect, useDisconnect |
| Read the active account and status | useConnection |
| Switch the active chain | useSwitchChain |
| Read contract state | useReadContract, useReadContracts |
| Read a native or token balance | useBalance |
| Send a transaction or call a contract | useWriteContract, useSendTransaction |
| Wait for a confirmation | useWaitForTransactionReceipt |
| Simulate a write before signing | useSimulateContract |
| Sign a message | useSignMessage |
| Resolve an ENS name | useEnsName, useEnsAddress |
| React to contract events | useWatchContractEvent |
What Trips People Up?
Most problems are setup or version issues, not application logic.
- Skipping
QueryClientProvider. Without it, every hook throws about a missing query client. Both providers are required, in that order. - Copying v1 or v2 code.
WagmiConfig,createClient, anduseAccountbelong to older versions. On v3 the equivalents areWagmiProvider,createConfig, anduseConnection. - Treating balances as numbers. Contract return values and balances are
bigint. Mixing them with ordinary numbers either throws or rounds wrong. - Pointing every chain at a shared default endpoint. Public defaults are rate-limited, and a burst of reads starts returning errors. A dedicated endpoint per chain avoids it — our Ethereum integration guide walks through choosing one and the viem-versus-ethers decision underneath.
- Forgetting server-side rendering. In Next.js the hooks run on the client, so set up cookie-based hydration or guard the server render, or account state flickers on first load.
Conclusion
A React dApp frontend with wagmi is, at its core, config, providers, and hooks. Declare your chains and transports, wrap the app in WagmiProvider and QueryClientProvider, then let useConnection, useReadContract, and useWriteContract handle connection, reads, and writes while the library keeps everything in sync.
Two habits save the most time. Use the v3 API — createConfig, WagmiProvider, useConnection — rather than snippets built on older versions, and point your transports at an endpoint you control before you ship. Test on Sepolia, keep API keys out of your source, and the move to mainnet is a single URL change.
FAQ
Is the library React-only?
No. The React hooks are the most common entry point, but the same core powers official Vue and Solid packages, plus a framework-agnostic @wagmi/core for vanilla JavaScript. The ideas — config, connectors, transports — carry across all of them.
Do I still need viem or ethers.js?
viem ships with it and does the low-level work, so you rarely call viem directly for everyday reads and writes. You don’t need ethers.js at all, though an adapter exists if some library you depend on still expects an ethers signer or provider.
Does it work on chains other than Ethereum?
Yes, on EVM-compatible networks. Add a chain and a transport for it — Polygon, Arbitrum, Optimism, BNB Smart Chain, Base, and others — and the same hooks apply. Switching the active chain from the UI is handled by useSwitchChain.
Is it free and open source?
Yes. The project is MIT-licensed and free to use. Your costs come from the endpoint your app connects through and any paid wallet services you add, not from the library itself.
How is it different from RainbowKit?
RainbowKit is a prebuilt, styled wallet-connection UI that runs on top of wagmi. wagmi gives you the hooks and state; RainbowKit — or ConnectKit, or Reown AppKit — gives you a ready-made connect button and modal so you don’t build that interface from scratch.



