ethers vs web3 vs wagmi vs hardhat vs truffle vs moralis vs viem
Ethereum Development Libraries for Frontend Applications
ethersweb3wagmihardhattrufflemoralisviemSimilar Packages:

Ethereum Development Libraries for Frontend Applications

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.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
ethers2,330,4218,65513 MB6343 months agoMIT
web3584,73719,9603.46 MB144a year agoLGPL-3.0
wagmi433,9266,6741.84 MB23a month agoMIT
hardhat342,5238,4453.31 MB6246 days agoMIT
truffle19,98113,98059.2 MB5273 years agoMIT
moralis038211.8 MB92 years agoMIT
viem03,42121.8 MB274 days agoMIT

Ethereum Development Libraries: Choosing the Right Tool for Frontend Integration

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.

๐Ÿงฑ Core Architecture: Direct RPC vs Abstraction Layers

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();

๐Ÿ”Œ Wallet Integration Patterns

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>;
}

โš™๏ธ Smart Contract Interaction Models

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 });

๐Ÿ“ฆ Bundle Size and Tree-Shaking Considerations

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

๐Ÿงช Testing and Development Environment Integration

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')
});

๐Ÿ”„ State Management Approaches

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 })
);

๐ŸŽฏ When to Use Each Library

For New React Applications

  • wagmi + viem: Best combination for modern React dApps with excellent TypeScript support and minimal boilerplate
  • Avoid web3 and truffle for new projects due to bundle size and maintenance concerns

For Vanilla JavaScript Applications

  • ethers: Most mature option with excellent documentation and community support
  • viem: Better choice if you prioritize TypeScript safety and tree-shaking

For Rapid Prototyping

  • moralis: Fastest path to working applications when you don't need direct blockchain access
  • Trade-off: Centralized dependency and potential vendor lock-in

For Development and Testing

  • hardhat: Industry standard for smart contract development and testing
  • truffle: Only for maintaining existing projects; not recommended for new development

โš ๏ธ Important Considerations

truffle 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.

๐Ÿ“Š Summary Table

LibraryPrimary Use CaseReact-SpecificTypeScript SupportBundle SizeMaintenance Status
ethersDirect blockchain interactionโŒโœ… (v6 improved)MediumActively maintained
viemType-safe blockchain interactionโŒโœ…โœ…โœ…Small (tree-shakable)Actively maintained
wagmiReact dApp developmentโœ…โœ…โœ…โœ…Medium (with React overhead)Actively maintained
web3Legacy blockchain interactionโŒโš ๏ธ (limited)LargeMaintained but legacy
moralisCentralized Web3 APIโœ… (React SDK)โœ…MediumActively maintained
hardhatDevelopment environmentโŒโœ…N/A (not for frontend)Actively maintained
truffleLegacy developmentโŒโš ๏ธN/A (not for frontend)Maintained but legacy

๐Ÿ’ก Final Recommendation

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.

How to Choose: ethers vs web3 vs wagmi vs hardhat vs truffle vs moralis vs viem

  • ethers:

    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.

  • web3:

    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.

  • wagmi:

    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.

  • hardhat:

    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.

  • truffle:

    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.

  • moralis:

    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.

  • viem:

    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.

README for ethers

The Ethers Project

npm (tag) CI Tests npm bundle size (version) npm (downloads) GitPOAP Badge Twitter Follow


A complete, compact and simple library for Ethereum and ilk, written in TypeScript.

Features

  • Keep your private keys in your client, safe and sound
  • Import and export JSON wallets (Geth, Parity and crowdsale)
  • Import and export BIP 39 mnemonic phrases (12 word backup phrases) and HD Wallets (English as well as Czech, French, Italian, Japanese, Korean, Simplified Chinese, Spanish, Traditional Chinese)
  • Meta-classes create JavaScript objects from any contract ABI, including ABIv2 and Human-Readable ABI
  • Connect to Ethereum nodes over JSON-RPC, INFURA, Etherscan, Alchemy, Ankr or MetaMask
  • ENS names are first-class citizens; they can be used anywhere an Ethereum addresses can be used
  • Small (~144kb compressed; 460kb uncompressed)
  • Tree-shaking focused; include only what you need during bundling
  • Complete functionality for all your Ethereum desires
  • Extensive documentation
  • Large collection of test cases which are maintained and added to
  • Fully written in TypeScript, with strict types for security and safety
  • MIT License (including ALL dependencies); completely open source to do with as you please

Keep Updated

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

Installing

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>

Documentation

Browse the documentation online:

Providers

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:

Extension Packages

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.

  • MulticallProvider - A Provider which bundles multiple call requests into a single call to reduce latency and backend request capacity
  • MulticoinPlugin - A Provider plugin to expand the support of ENS coin types
  • GanaceProvider - A Provider for in-memory node instances, for fast debugging, testing and simulating blockchain operations
  • Optimism Utilities - A collection of Optimism utilities
  • LedgerSigner - A Signer to interact directly with Ledger Hardware Wallets

License

MIT License (including all dependencies).