All Insights
Jan 202610 min read

Why We Switched to Viem

A practical look at migrating our entire Web3 stack from Web3.js to Viem — what worked, what didn't, and whether we recommend it.

Gabriel Njoabozia
Gabriel NjoaboziaFounder & Lead Engineer

Background

We've been building Web3 products since 2021. For most of that time, Web3.js was the default choice for Ethereum interactions. It worked, but it came with friction — large bundle sizes, TypeScript struggles, and an API that felt dated.

When Viem launched, we were skeptical. Another Ethereum library? But after hearing consistently positive feedback from the community and seeing Wagmi (which we were already using) adopt it as its core, we decided to evaluate it seriously.

The Migration Process

Our migration took place over two weeks, affecting three production applications. Here's what we learned.

Bundle Size: The Immediate Win

This was the first thing we noticed. Viem's tree-shakeable architecture means you only ship what you use.

// Before: Web3.js
import Web3 from 'web3'
const web3 = new Web3(rpcUrl)
// Bundle: ~140KB gzipped

// After: Viem
import { createPublicClient, http } from 'viem'
const client = createPublicClient({ transport: http(rpcUrl) })
// Bundle: ~30KB gzipped

For our applications, this translated to ~45% reduction in Web3-related bundle size. Significant, especially on mobile.

TypeScript: Actually Works Now

Web3.js TypeScript support was... optimistic at best. Viem was built with TypeScript from day one, and it shows.

// TypeScript actually catches mistakes now
import { erc20Abi } from 'viem'

const balance = await client.readContract({
  address: tokenAddress,
  abi: erc20Abi, // Fully typed ABI
  functionName: 'balanceOf',
  args: [address], // TypeScript knows this needs an address
})

// Try passing a string to args that expects an array?
// TypeScript: "Type 'string' is not assignable to type 'Address'"

The API: Cleaner, More Predictable

Viem's API follows a consistent pattern. Actions are grouped logically, and the naming is intuitive.

// Web3.js - inconsistent methods
const balance = await contract.methods.balanceOf(address).call()
const tx = await web3.eth.sendTransaction({...})
const gas = await web3.eth.estimateGas({...})

// Viem - consistent pattern
const balance = await client.readContract({...})
const txHash = await client.sendTransaction({...})
const gas = await client.estimateGas({...})

What Was Tricky

No migration is painless. Here were our friction points:

  • Custom ABIs — We had complex custom ABIs that needed rewriting. Not hard, just tedious.
  • Event parsing — Viem's event decoding works differently. Took some adjustment.
  • Provider differences — Viem doesn't include a provider. You need wagmi or a separate provider setup.
  • Historical reads — Some patterns for fetching historical data needed rethinking.

Performance Improvements

Beyond bundle size, we saw measurable performance improvements:

  • ~20% faster initial RPC calls due to optimized transport layer
  • Better request batching with VIEM's public client
  • More reliable reconnection handling
  • Reduced memory footprint

Would We Recommend It?

Yes. If you're starting a new Web3 project, use Viem. If you're maintaining a Web3.js project, migrate when you have bandwidth.

The bundle size reduction alone justifies the switch. The TypeScript improvements alone justify the switch. Combined with better performance and a more maintainable codebase, it's not even close.

Migration Checklist

If you're planning a migration:

  • Set aside dedicated time (estimate 1-2 weeks for full migration)
  • Write tests for critical transaction flows before starting
  • Migrate one application at a time
  • Use wagmi if you're using React — the integration is seamless
  • Keep Web3.js around during transition for edge cases

Building with Web3 and considering a stack change? We've migrated dozens of projects and can help you plan the transition.

Let's discuss your architecture →

Building a Web3 product? Let's talk →