Web3 Development · 5 min read ·
Build fast, reliable EVM dapps using wagmi hooks and viem clients—wallet connect, reads, writes, and patterns for production-grade frontends.
If you’re building an EVM dapp frontend in 2026, the “good enough” era is over. Users expect instant reads, clear transaction states, and resilient wallet flows across multiple chains. The wagmi + viem combo hits the sweet spot: wagmi provides React-first state management and wallet ergonomics; viem provides a type-safe, composable RPC client that avoids a lot of the footguns you might remember from older stacks.
At ChainMagic Studio, we tend to recommend this pairing for most consumer-facing dapps because it scales from prototypes to production without a rewrite. The key is to treat your frontend as a first-class client: typed ABIs, cached reads, explicit simulation before writes, and predictable transaction lifecycle UI.
A practical mental model:
createPublicClient, createWalletClient, ABI encoding/decoding, logs, events, and utilities.In wagmi v2+, viem is the underlying engine. You still interact primarily through wagmi hooks in the UI, but understanding viem helps when you need custom RPC calls, batch reads, log queries, or server-side tasks.
A standard approach:
A typical provider tree:
WagmiProvider with a configQueryClientProviderThe important production detail: define transports per chain and consider a fallback strategy (e.g., a paid RPC + public RPC) so your app doesn’t collapse when a single endpoint rate-limits.
Wallet connection UX is where many dapps still leak complexity. A “boring” implementation:
wagmi gives you useAccount, useConnect, useDisconnect, and useSwitchChain. Your UI should treat connection as state, not as an event.
Opinionated guidance:
Most dapp screens are read-heavy: balances, allowances, positions, pool states, NFTs, etc. wagmi’s useReadContract and useReadContracts cover the common cases.
Patterns we recommend:
useReadContracts for dashboards. It reduces waterfall latency and avoids rendering 6 spinners.query.enabled (or wagmi’s enabled) so you don’t query before you have an address/chain.A concrete example: for an ERC-20 “Deposit” screen, you typically need:
balanceOf(user)allowance(user, spender)decimals and symbolFetch static metadata once and cache aggressively; refresh balances when a transaction lands.
The biggest improvement you can make to transaction UX is preflight simulation. viem and wagmi support this cleanly.
Best practice flow:
simulateContract) to catch reverts and estimate gas with realistic calldata.writeContract) using the request returned from simulation.This avoids the classic “user signs, then it fails” trap and surfaces revert reasons early. In wagmi, you can implement this with useSimulateContract + useWriteContract, or do it imperatively in a handler if you prefer.
Also: be explicit about amounts. Always normalize input strings to bigint using token decimals (viem utilities like parseUnits and formatUnits help). If your UI mixes floats with onchain integers, you will eventually ship a rounding bug.
A professional dapp UI recognizes these distinct states:
wagmi gives you primitives like useWaitForTransactionReceipt to track confirmation. You should also handle replacement (speed-ups/cancels) gracefully: if the user replaces the transaction, your UI must not stay stuck on the old hash.
Practical tip: store “active transaction” in a small client state store keyed by chainId and user address, and clear it on receipt.
It’s tempting to build activity feeds by querying logs directly from RPC (viem can do it), but it doesn’t scale well for historical queries, mobile clients, or rate limits.
A sane approach:
Your frontend stack remains wagmi + viem; the indexing layer is complementary. The key is not to confuse “possible” with “production-grade.”
Multi-chain dapps often fail in subtle ways: wrong RPC, mismatched contract addresses, and broken token metadata.
Recommendations:
contracts map keyed by chainId.When you switch chains, invalidate or refetch relevant queries (wagmi helps because queries are scoped by chain in many cases, but your own caches might not be).
A few hard-earned lessons:
wagmi + viem is a pragmatic Web3 frontend stack because it draws a clean line: wagmi owns React-friendly state and wallet ergonomics, while viem provides a fast, type-safe foundation for EVM interactions. If you adopt the production patterns—batch reads, simulate-before-write, explicit transaction state UX, and a sensible indexing strategy—you’ll ship dapps that feel responsive and trustworthy.
The strongest teams treat blockchain integration like any other distributed system: unreliable networks, partial failures, and asynchronous state changes. wagmi and viem won’t remove that complexity, but they give you the right primitives to manage it cleanly—and that’s what “good Web3 UX” actually means.