ethers, viem, and web3 are low-level JavaScript libraries that provide direct access to Ethereum blockchain functionality through JSON-RPC calls, enabling developers to interact with smart contracts, accounts, and transactions. wagmi is a React-specific library built on top of viem that provides hooks for common wallet and blockchain interactions. hardhat and truffle are development environments primarily used for smart contract compilation, testing, and deployment rather than frontend integration. moralis offers a higher-level API that abstracts away direct blockchain calls by providing REST endpoints and SDKs for common Web3 operations like authentication, token balances, and transaction history.
When building decentralized applications (dApps), developers face a critical decision: which library to use for blockchain interactions. The ecosystem has evolved significantly, with some tools focusing on direct blockchain communication, others on developer experience, and still others on abstracting away blockchain complexity entirely. Let's examine how these seven packages differ in real-world usage.
ethers, viem, and web3 provide direct JSON-RPC interfaces to Ethereum nodes. They handle the low-level communication but differ significantly in design philosophy.
// ethers: Provider and Signer pattern
import { ethers } from 'ethers';
const provider = new ethers.JsonRpcProvider('https://mainnet.infura.io/v3/YOUR_KEY');
const contract = new ethers.Contract(address, abi, provider);
const result = await contract.myFunction();
// viem: Client-based architecture with TypeScript-first design
import { createPublicClient, http } from 'viem';
import { mainnet } from 'viem/chains';
const client = createPublicClient({ chain: mainnet, transport: http() });
const result = await client.readContract({ address, abi, functionName: 'myFunction' });
// web3: Legacy callback-style with promise support
import Web3 from 'web3';
const web3 = new Web3('https://mainnet.infura.io/v3/YOUR_KEY');
const contract = new web3.eth.Contract(abi, address);
const result = await contract.methods.myFunction().call();
wagmi builds on viem specifically for React applications:
// wagmi: React hooks for common patterns
import { useContractRead } from 'wagmi';
function MyComponent() {
const { data, isLoading } = useContractRead({
address,
abi,
functionName: 'myFunction'
});
return <div>{isLoading ? 'Loading...' : data}</div>;
}
moralis completely abstracts away direct blockchain calls:
// moralis: REST API wrapper
import Moralis from 'moralis';
await Moralis.start({ apiKey: 'YOUR_KEY' });
const response = await Moralis.EvmApi.token.getTokenBalances({
address: '0x...',
chain: '0x1'
});
hardhat and truffle are primarily development environments:
// hardhat: Development-focused, not for production frontends
import { ethers } from 'hardhat'; // Only works in Hardhat runtime
const contract = await ethers.getContract('MyContract');
// truffle: Contract artifacts for testing/deployment
const MyContract = artifacts.require('MyContract');
const instance = await MyContract.deployed();
How each library handles wallet connections reveals their intended use cases.
ethers requires manual wallet provider setup:
// ethers: Manual wallet handling
import { BrowserProvider } from 'ethers';
if (window.ethereum) {
const provider = new BrowserProvider(window.ethereum);
await provider.send('eth_requestAccounts', []);
const signer = await provider.getSigner();
}
viem provides explicit account management:
// viem: Explicit account injection
import { createWalletClient, custom } from 'viem';
const walletClient = createWalletClient({
transport: custom(window.ethereum!)
});
const [account] = await walletClient.getAddresses();
wagmi automates this with React context:
// wagmi: Automatic wallet state management
import { useAccount, useConnect, useDisconnect } from 'wagmi';
import { injected } from 'wagmi/connectors';
function ConnectButton() {
const { address, isConnected } = useAccount();
const { connect } = useConnect({ connector: injected() });
const { disconnect } = useDisconnect();
if (isConnected) return <button onClick={() => disconnect()}>Disconnect</button>;
return <button onClick={() => connect()}>Connect</button>;
}
moralis uses its own authentication flow:
// moralis: Centralized authentication
import { useMoralis } from 'react-moralis';
function LoginButton() {
const { authenticate, isAuthenticated, logout } = useMoralis();
if (isAuthenticated) return <button onClick={logout}>Logout</button>;
return <button onClick={authenticate}>Login</button>;
}
The way libraries handle contract interactions affects both developer experience and bundle size.
ethers uses contract instances:
// ethers: Contract instance approach
const contract = new ethers.Contract(address, abi, signer);
const tx = await contract.setSomething('value');
await tx.wait();
viem uses function calls with explicit parameters:
// viem: Explicit function call
import { writeContract } from 'viem/actions';
const hash = await writeContract(walletClient, {
address,
abi,
functionName: 'setSomething',
args: ['value']
});
wagmi provides specialized hooks:
// wagmi: React hooks for contract writes
import { useContractWrite } from 'wagmi';
function SetButton() {
const { write } = useContractWrite({
address,
abi,
functionName: 'setSomething'
});
return <button onClick={() => write?.({ args: ['value'] })}>Set</button>;
}
web3 uses method chaining:
// web3: Method chaining pattern
const tx = contract.methods.setSomething('value');
const gas = await tx.estimateGas({ from: account });
const receipt = await tx.send({ from: account, gas });
Modern frontend applications demand careful attention to bundle size.
viem is designed with tree-shaking in mind:
// viem: Import only what you need
import { createPublicClient, http } from 'viem';
// Only includes public client code, not wallet or test utilities
ethers v6 improved tree-shaking but still has larger base imports:
// ethers: More granular imports in v6
import { Contract } from 'ethers';
// Still includes more core functionality by default
web3 has historically struggled with bundle size:
// web3: Large monolithic import
import Web3 from 'web3';
// Includes all functionality even if unused
wagmi adds React-specific overhead but provides significant DX benefits:
// wagmi: React context and hooks add size but reduce boilerplate
import { WagmiConfig, createConfig } from 'wagmi';
// Includes React context providers and hook implementations
Some libraries blur the line between frontend and development tooling.
hardhat is fundamentally a development environment:
// hardhat: Designed for testing, not production
// Hardhat Runtime Environment only available during tests/tasks
const { ethers } = require('hardhat');
// Cannot be used in browser applications directly
truffle follows a similar pattern:
// truffle: Contract artifacts for deployment/testing
// Artifacts are generated during compilation, not for browser use
const MyContract = artifacts.require('MyContract');
ethers, viem, and web3 can work with local development networks:
// ethers with local network
const provider = new ethers.JsonRpcProvider('http://localhost:8545');
// viem with local network
const client = createPublicClient({
chain: localhost, // from viem/chains
transport: http('http://localhost:8545')
});
How libraries handle blockchain state affects application architecture.
ethers and viem are stateless:
// ethers: Manual state management required
const [balance, setBalance] = useState();
useEffect(() => {
provider.getBalance(address).then(setBalance);
}, [address]);
// viem: Similarly stateless
const [balance, setBalance] = useState();
useEffect(() => {
publicClient.getBalance({ address }).then(setBalance);
}, [address]);
wagmi provides built-in state management:
// wagmi: Automatic state management
const { data: balance } = useBalance({ address });
// Handles loading states, caching, and revalidation automatically
moralis also manages state internally:
// moralis: Built-in state management
const { data } = useMoralisQuery('balances', () =>
Moralis.EvmApi.account.getAccountBalance({ address })
);
wagmi + viem: Best combination for modern React dApps with excellent TypeScript support and minimal boilerplateweb3 and truffle for new projects due to bundle size and maintenance concernsethers: Most mature option with excellent documentation and community supportviem: Better choice if you prioritize TypeScript safety and tree-shakingmoralis: Fastest path to working applications when you don't need direct blockchain accesshardhat: Industry standard for smart contract development and testingtruffle: Only for maintaining existing projects; not recommended for new developmenttruffle should generally not be used in frontend applications. Its contract artifacts and runtime are designed for Node.js environments during development and testing, not for browser deployment.
hardhat's ethers plugin provides the same ethers library that would be used in production, but the Hardhat runtime itself is not intended for browser use.
web3 remains widely used in legacy applications but has been largely superseded by more modern alternatives that offer better TypeScript support, smaller bundle sizes, and more intuitive APIs.
| Library | Primary Use Case | React-Specific | TypeScript Support | Bundle Size | Maintenance Status |
|---|---|---|---|---|---|
ethers | Direct blockchain interaction | โ | โ (v6 improved) | Medium | Actively maintained |
viem | Type-safe blockchain interaction | โ | โ โ โ | Small (tree-shakable) | Actively maintained |
wagmi | React dApp development | โ | โ โ โ | Medium (with React overhead) | Actively maintained |
web3 | Legacy blockchain interaction | โ | โ ๏ธ (limited) | Large | Maintained but legacy |
moralis | Centralized Web3 API | โ (React SDK) | โ | Medium | Actively maintained |
hardhat | Development environment | โ | โ | N/A (not for frontend) | Actively maintained |
truffle | Legacy development | โ | โ ๏ธ | N/A (not for frontend) | Maintained but legacy |
For new frontend applications, the clear winners are viem for vanilla JavaScript/TypeScript projects and wagmi for React applications. Both provide modern, well-maintained APIs with excellent TypeScript support and reasonable bundle sizes.
Avoid using hardhat and truffle in production frontend code โ they're development tools, not frontend libraries. Use moralis only when you specifically need its centralized API services and are comfortable with the trade-offs of vendor dependency.
The era of heavy, monolithic Web3 libraries is over. Modern development favors composable, type-safe, and tree-shakable alternatives that integrate cleanly with contemporary frontend frameworks and build tools.
Choose ethers if you need a mature, well-documented library with a clean API for direct Ethereum interactions without React dependencies. It's ideal for vanilla JavaScript applications or when you want fine-grained control over provider and signer management without the overhead of React-specific abstractions.
Choose web3 only if you're working with legacy codebases that already depend on it, as it's a heavier library with a more complex API compared to modern alternatives. For new projects, prefer ethers or viem which offer better TypeScript support, smaller bundle sizes, and more intuitive APIs.
Choose wagmi if you're building React applications and want pre-built hooks for common Web3 patterns like wallet connection, contract reads/writes, and chain switching. It significantly reduces boilerplate code for React-based dApps but requires you to use React and adds an abstraction layer over direct blockchain interactions.
Choose hardhat if your primary need is smart contract development, testing, and deployment rather than frontend integration. While it can be used in frontend contexts through its network provider, it's primarily designed as a development environment and should not be your main choice for production frontend applications.
Choose truffle only if you're maintaining legacy projects, as it's primarily focused on smart contract development workflows rather than frontend integration. For new frontend applications, prefer more modern alternatives like ethers or viem that are actively maintained and optimized for browser environments.
Choose moralis if you want to avoid direct blockchain interactions and prefer a centralized API service that handles complex queries like transaction history, token balances, and NFT metadata. It's suitable when you need rapid development with minimal blockchain knowledge, but be aware that it introduces a dependency on Moralis' infrastructure.
Choose viem if you're building modern TypeScript applications and want type-safe, tree-shakable Ethereum interactions with excellent performance characteristics. It's particularly strong when you need precise control over RPC methods and want to avoid the bundle size overhead of larger libraries while maintaining compatibility with EIP standards.
A complete, compact and simple library for Ethereum and ilk, written in TypeScript.
Features
For advisories and important notices, follow @ethersproject on Twitter (low-traffic, non-marketing, important information only) as well as watch this GitHub project.
For more general news, discussions, and feedback, follow or DM me, @ricmoo on Twitter or on the Ethers Discord.
For the latest changes, see the CHANGELOG.
Summaries
NodeJS
/home/ricmoo/some_project> npm install ethers
Browser (ESM)
The bundled library is available in the ./dist/ folder in this repo.
<script type="module">
import { ethers } from "./dist/ethers.min.js";
</script>
Browse the documentation online:
Ethers works closely with an ever-growing list of third-party providers to ensure getting started is quick and easy, by providing default keys to each service.
These built-in keys mean you can use ethers.getDefaultProvider() and
start developing right away.
However, the API keys provided to ethers are also shared and are intentionally throttled to encourage developers to eventually get their own keys, which unlock many other features, such as faster responses, more capacity, analytics and other features like archival data.
When you are ready to sign up and start using for your own keys, please check out the Provider API Keys in the documentation.
A special thanks to these services for providing community resources:
The ethers package only includes the most common and most core
functionality to interact with Ethereum. There are many other
packages designed to further enhance the functionality and experience.
call to reduce latency and backend request capacityMIT License (including all dependencies).