Developer Guide — Connect Web3 & Ethers.js to MetaMask

Try Tangem secure wallet →

Developer Guide — Connect Web3 & Ethers.js to MetaMask

Table of contents


Introduction

This guide explains practical ways to connect a Web3-enabled website or dApp to MetaMask using Ethers.js and Web3.js. I'll show clear, testable examples for front-end JavaScript, React, and local development (Ganache and Remix). I believe hands-on examples are the fastest way to learn. I've been using these patterns daily while building DeFi integrations and testing smart contracts.

What you'll get: working code snippets, event handling tips, and security reminders (like token approvals and gas settings). And yes — we'll cover both Ethers.js v5 and v6 so you can use whichever your project requires.

Quick connection overview

There are three common ways a website connects to a user's software wallet:

Method Pros Cons
Injected provider (window.ethereum) Fast, user-friendly, supports signing transactions Requires wallet installed and unlocked
WalletConnect Mobile-friendly, works without extension Extra UX step, session handling
RPC fallback Good for read-only calls (balances) Cannot sign user transactions

If you want to learn how to add the extension or mobile app to your environment first, see the setup guide: /install-metamask-extension and /install-metamask-mobile-app.

Ethers.js: step-by-step (v5 and v6)

Ethers.js is a popular library for interacting with Ethereum and EVM-compatible chains. Below are minimal patterns for requesting account access and getting a signer.

Ethers v5 example

// Ethers v5
import { ethers } from 'ethers';

async function connectEthersV5() {
  if (!window.ethereum) throw new Error('No injected provider');

  const provider = new ethers.providers.Web3Provider(window.ethereum);

  // Request account access
  await provider.send('eth_requestAccounts', []);

  const signer = provider.getSigner();
  const address = await signer.getAddress();
  console.log('Connected address', address);

  return { provider, signer };
}

Ethers v6 example

// Ethers v6
import { ethers } from 'ethers';

async function connectEthersV6() {
  if (!window.ethereum) throw new Error('No injected provider');

  const provider = new ethers.BrowserProvider(window.ethereum);

  // Request account access
  await provider.send('eth_requestAccounts', []);

  const signer = await provider.getSigner();
  const address = await signer.getAddress();
  console.log('Connected address', address);

  return { provider, signer };
}

These examples cover the common keyword patterns: ethers js connect metamask, ethers.js connect metamask, and connect metamask javascript.

Web3.js: basic connect flow

If your project uses Web3.js, the flow is similar but uses the Web3 provider wrapper.

import Web3 from 'web3';

async function connectWeb3js() {
  if (!window.ethereum) throw new Error('No injected provider');

  await window.ethereum.request({ method: 'eth_requestAccounts' });
  const web3 = new Web3(window.ethereum);
  const accounts = await web3.eth.getAccounts();
  console.log('Account', accounts[0]);
  return { web3, accounts };
}

This addresses connect metamask web3js and web3 connect metamask queries.

React integration patterns

React apps often need to manage provider state and clean up listeners. Here’s a minimal pattern using hooks.

import React, { useState, useEffect } from 'react';
import { ethers } from 'ethers';

function useMetaMask() {
  const [address, setAddress] = useState(null);
  const [provider, setProvider] = useState(null);

  useEffect(() => {
    if (!window.ethereum) return;
    const p = new ethers.providers.Web3Provider(window.ethereum);
    setProvider(p);

    const handleAccounts = (accounts) => setAddress(accounts[0] || null);

    window.ethereum.on('accountsChanged', handleAccounts);

    return () => {
      window.ethereum.removeListener('accountsChanged', handleAccounts);
    };
  }, []);

  return { provider, address };
}

That pattern helps with connect metamask react implementations (see also /connect-web3-react for library integrations).

Local development: Ganache & Remix

Want to test locally? Two common workflows:

But take care: if you import a private key into your daily-use software wallet, only do so in a disposable account.

Handling accounts, chain changes, and events

The wallet emits events you must handle: accountsChanged, chainChanged, and sometimes disconnect. Use them to keep UI state consistent.

window.ethereum.on('accountsChanged', (accounts) => { /* update UI */ });
window.ethereum.on('chainChanged', (chainId) => { window.location.reload(); });

What if the user rejects the request? Catch the thrown error and show a clear prompt. (A modal that explains why you need the address often helps.)

Security & UX best practices for dApps

Personally, I once approved an unlimited allowance for a token while testing and had to revoke it later — a small mistake that was an expensive lesson. Keep wallets for daily use and hardware devices for large holdings.

Account abstraction and smart contract wallets (e.g., session keys and batched transactions) can improve UX for gasless or staged actions. Learn more: /smart-contract-wallets-aa and /account-abstraction.

Troubleshooting common errors

For UI-level problems with connect buttons and session persistence, check /connect-button-troubleshoot and /troubleshooting-dapp-connections.

Who this software wallet is best for / Who should look elsewhere

Who this is good for:

Who should look elsewhere:

Conclusion & next steps

Connecting a website to the injected provider is straightforward once you understand provider patterns and event handling. Test everything on a testnet or local node before launching to mainnet. If you're building a React app, check out /connect-web3-react and the deeper developer docs at /developer-integration and /metamask-api-connect.

Try the example code above in a small sandbox project. What I've found is that a simple connect button and clear error handling cover 90% of user issues. But keep iterating on UX and be strict about approvals and gas prompts.

Further reading and tools: /connect-remix, /connect-ganache-local, and the token approvals guide /token-approvals-revoke.

Happy building — and test with a disposable account first.


Try Tangem secure wallet →