Wagmi and viem: Fast Blockchain Integration for React Sites

Imagine your React app uses ethers.js to interact with contracts. When the user switches networks, the data doesn't update, and N+1 requests to the RPC choke the application. We've encountered this many times — hydration mismatches in Next.js, memory leaks from ethers.js mutable state. The solution

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1287
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1250
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    986
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    1038
  • image_website-sbh_0.webp
    Website development for SBH Partners
    1112
  • image_website-_0.webp
    Website development for Red Pear
    556

Imagine your React app uses ethers.js to interact with contracts. When the user switches networks, the data doesn't update, and N+1 requests to the RPC choke the application. We've encountered this many times — hydration mismatches in Next.js, memory leaks from ethers.js mutable state. The solution is the wagmi + viem combo. Our Web3 development experience spans over 5 years, and we've implemented this solution in 10+ DeFi projects. Switching to wagmi/viem reduces code volume by 2–3x and decreases TTFB by 40%, and lowers RPC request costs thanks to automatic caching.

Why ethers.js Slows Things Down

ethers.js is a class-oriented library with mutable state. Each new Contract(...) creates an object tied to a provider. In React, this is problematic: when the network or account changes, the entire object must be recreated, otherwise data doesn't update. Viem is built on functions and immutable clients. Wagmi automatically manages the client lifecycle — hooks update on any wallet changes. According to our stats, switching to wagmi/viem reduces code volume by 2–3x and decreases TTFB by 40%.

Comparison: ethers.js vs wagmi/viem

Criteria ethers.js wagmi + viem
State Mutable, objects live in context Immutable, no recreation needed
React integration Manual via useState/useEffect Built-in hooks with caching
Update on network change Need to subscribe to events Automatic refetch via watch
Typing Weak, manual casts Full typing via @wagmi/cli
Performance Higher latency due to class Lower TTFB due to lazy calls

How wagmi/viem Solve ethers.js Problems

Wagmi hooks automatically subscribe to wallet events (chainChanged, accountsChanged). This eliminates stale data issues. Viem clients are immutable — each call returns a fresh result. Together they provide reactivity without memory leaks. For example, useReadContract automatically refetches data on network change, and useWatchContractEvent filters events without creating new subscriptions on re-render. We use this approach in production projects, reducing RPC load and improving user experience. As noted in the official viem documentation, "viem is designed to be modular, tree-shakeable, and type-safe out of the box".

What ABI Type Generation Brings

Without type generation, each contract call requires manually specifying the ABI and casting results. Wagmi CLI generates types from your ABI files in one command. After generation, you get hooks with full typing of arguments and return values. This eliminates errors in addresses, arguments, and allows the editor to suggest possible values. Simply put, you import useReadErc20BalanceOf and pass the required address — everything is checked at compile time.

npm i -D @wagmi/cli npx wagmi generate 

How We Implement wagmi/viem: Step by Step

Configuration Setup

// lib/wagmi.ts import { createConfig, http } from 'wagmi'; import { mainnet, arbitrum, base } from 'wagmi/chains'; import { injected, walletConnect } from 'wagmi/connectors'; export const config = createConfig({ chains: [mainnet, arbitrum, base], connectors: [ injected(), walletConnect({ projectId: process.env.NEXT_PUBLIC_WC_PROJECT_ID! }), ], transports: { [mainnet.id]: http(process.env.ETH_RPC_URL!), [arbitrum.id]: http(process.env.ARBITRUM_RPC_URL!), [base.id]: http(process.env.BASE_RPC_URL!), }, }); 

Reading Data from a Contract

// hooks/useTokenData.ts import { useReadContracts } from 'wagmi'; import { erc20Abi, formatUnits } from 'viem'; export function useTokenData(tokenAddress: `0x${string}`, userAddress?: `0x${string}`) { const { data, isLoading } = useReadContracts({ contracts: [ { address: tokenAddress, abi: erc20Abi, functionName: 'name' }, { address: tokenAddress, abi: erc20Abi, functionName: 'symbol' }, { address: tokenAddress, abi: erc20Abi, functionName: 'decimals' }, { address: tokenAddress, abi: erc20Abi, functionName: 'totalSupply' }, ...(userAddress ? [{ address: tokenAddress, abi: erc20Abi, functionName: 'balanceOf' as const, args: [userAddress] as [`0x${string}`], }] : []), ], query: { refetchInterval: 30_000, staleTime: 10_000, }, }); const decimals = (data?.[2].result as number) ?? 18; return { isLoading, name: data?.[0].result as string | undefined, symbol: data?.[1].result as string | undefined, decimals, totalSupply: data?.[3].result ? formatUnits(data[3].result as bigint, decimals) : undefined, userBalance: userAddress && data?.[4]?.result ? formatUnits(data[4].result as bigint, decimals) : undefined, }; } 

Writing to a Contract with Simulation

// hooks/useTokenTransfer.ts import { useWriteContract, useWaitForTransactionReceipt, useSimulateContract } from 'wagmi'; import { erc20Abi, parseUnits } from 'viem'; import { useState } from 'react'; export function useTokenTransfer(tokenAddress: `0x${string}`, decimals: number) { const [recipient, setRecipient] = useState<`0x${string}` | undefined>(); const [amount, setAmount] = useState(''); const amountWei = amount ? parseUnits(amount, decimals) : 0n; const { error: simError } = useSimulateContract({ address: tokenAddress, abi: erc20Abi, functionName: 'transfer', args: [recipient!, amountWei], query: { enabled: !!recipient && amountWei > 0n }, }); const { writeContract, data: txHash, isPending } = useWriteContract(); const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash: txHash }); const transfer = () => { if (!recipient || amountWei === 0n) return; writeContract({ address: tokenAddress, abi: erc20Abi, functionName: 'transfer', args: [recipient, amountWei], }); }; return { recipient, setRecipient, amount, setAmount, simError, transfer, txHash, isPending, isConfirming, isSuccess, }; } 

Server-Side viem Client

// lib/publicClient.ts import { createPublicClient, http, createWalletClient } from 'viem'; import { mainnet } from 'viem/chains'; import { privateKeyToAccount } from 'viem/accounts'; export const publicClient = createPublicClient({ chain: mainnet, transport: http(process.env.ETH_RPC_URL!), }); export const serverWallet = createWalletClient({ account: privateKeyToAccount(process.env.RELAYER_PRIVATE_KEY as `0x${string}`), chain: mainnet, transport: http(process.env.ETH_RPC_URL!), }); 

Type Generation from ABI

npm i -D @wagmi/cli npx wagmi generate 

After generation, hooks like useReadErc20BalanceOf(...) appear with full argument typing.

Example Usage in Next.js App Router

In server components, use viem directly since wagmi works only on the client. Create a publicClient and use it for data reads. For instance, in serverComponent.tsx, import publicClient from lib/publicClient.ts and call methods without React hooks.

Our Work Process

  • Audit — Analyze current implementation, identify issues (memory leaks, data staleness).
  • Design — Design the interaction layer: configs, hooks, server clients.
  • Implementation — Write code with type generation, cover edge cases.
  • Testing — Simulations, network tests, manual checks with various wallets.
  • Deployment — Deploy, monitor, document.

What's Included

  • Integration code with wagmi/viem: typed hooks.
  • Documentation for use and further development.
  • CI/CD setup for automatic ABI updates.
  • Team training (1–2 sessions).
  • Compatibility guarantee with MetaMask, WalletConnect, Coinbase Wallet.

Timelines

Basic integration (read/write for 1–2 contracts) — 1–2 days. Full layer with type generation, server clients, and event subscriptions — 2–3 days. Timelines are refined after auditing your project.

Order an Audit of Your Integration — We'll Find Bottlenecks in 1 Day

Approach Comparison: ethers.js vs wagmi/viem

Parameter ethers.js wagmi/viem
Boilerplate High (manual provider management) Low (automatic configuration)
Data update Manual via useEffect Automatic via watch
Type safety No Full (via @wagmi/cli)
Performance ~2 ms per call <1 ms per call (lazy evaluation)
Server-side support Complex (needs ethers.providers) Simple (viem createPublicClient)

Contact us — we'll discuss your project details and choose the optimal architecture.