dev-tooling/ethers-vs-viem

ethers.js vs viem vs wagmi Selection Guide

ethereumdev-tools👥 Communityconfidence highhealth 100%
v1.0.0·Updated 3/20/2026

Positioning

LibraryPositioningUse Cases
ethers.js v6Full-featured EVM library, longest historyBackend scripts, Node.js tools
viemLightweight, type-safe, high-performanceModern frontend/backend, ethers alternative
wagmiReact Hooks wrapper (built on viem)Top choice for React DApp frontends
web3.jsOldest library, gradually being phased outLegacy project maintenance only

Why viem is Replacing ethers

Bundle size: viem ~35KB vs ethers ~280KB (gzipped) Tree-shaking: Import only what you need, unused code excluded Type safety: Automatic ABI type inference, better IDE autocomplete Performance: Faster execution for equivalent requests

// ethers v6
const provider = new ethers.JsonRpcProvider(RPC_URL)
const balance = await provider.getBalance(address)

// viem (same functionality, more lightweight)
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'
const client = createPublicClient({ chain: mainnet, transport: http(RPC_URL) })
const balance = await client.getBalance({ address })

wagmi Complete Example (React DApp)

npm install wagmi viem @tanstack/react-query
// wagmi config
import { createConfig, http } from 'wagmi'
import { mainnet, base, arbitrum } from 'wagmi/chains'
import { injected, walletConnect } from 'wagmi/connectors'

export const config = createConfig({
  chains: [mainnet, base, arbitrum],
  connectors: [injected(), walletConnect({ projectId: 'YOUR_ID' })],
  transports: { [mainnet.id]: http(), [base.id]: http(), [arbitrum.id]: http() },
})

// Hooks usage
import { useAccount, useBalance, useReadContract, useWriteContract } from 'wagmi'

function MyDApp() {
  const { address, isConnected } = useAccount()
  const { data: balance } = useBalance({ address })
  const { data: tokenBal } = useReadContract({
    address: TOKEN_ADDRESS, abi: ERC20_ABI,
    functionName: 'balanceOf', args: [address!],
    query: { enabled: !!address }
  })
  const { writeContract, isPending } = useWriteContract()
  return (
    <button onClick={() => writeContract({
      address: TOKEN_ADDRESS, abi: ERC20_ABI,
      functionName: 'transfer', args: [recipient, amount]
    })} disabled={isPending}>Transfer</button>
  )
}

Decision Tree

What is your project?

  • React DApp frontend → wagmi (Hooks wrapper, ready out of the box)
  • Next.js/pure frontend, don't want Hooks → viem
  • Node.js backend scripts/CLI → viem or ethers v6
  • Maintaining legacy project (ethers v5) → Continue using, no rush to migrate
  • Maintaining legacy project (web3.js) → Use viem for new features

npm Downloads (2024)

  • ethers: ~2.5M/week (still highest, growth slowing)
  • viem: ~2M/week (fastest growth)
  • wagmi: ~700K/week (rapid growth)
  • web3.js: ~500K/week (declining)