Independent review. This site is not the official website and is not affiliated with, endorsed by, or operated by the wallet vendor reviewed here. Never enter your seed phrase or private keys on any third-party site.

Developer Integration — web3.js, ethers, React, Node, Python

Try Tangem secure wallet →

Introduction

This page focuses on developer integration patterns for MetaMask: how browser-based dApps ask a software wallet for accounts, signatures, and transactions. I use MetaMask daily for testing and small trades (so I speak from hands-on experience). What I've found is that most problems come from lifecycle events — accounts or chain changes — and from assuming the wallet behaves like a backend RPC node.

Who this guide is for

  • Frontend devs building dApps with plain JavaScript, React, or React Native.
  • Backend devs who need to verify signatures or accept signed transactions.

Who should look elsewhere

  • Teams building high-throughput backends that need custodial signing (use a server-side key manager instead).

If you haven't installed the extension or mobile app yet, see install-metamask-extension and install-metamask-mobile-app.

Try Tangem secure wallet →

How MetaMask exposes a provider (quick primer)

MetaMask injects an EIP-1193-compatible provider into web pages as window.ethereum. You request access with a method call (eg. eth_requestAccounts) and then subscribe to provider events (accountsChanged, chainChanged). Simple. But there are details: the provider is a bridge to the user's private keys in the hot wallet. Requests must be user-initiated for UX and security reasons.

Events you must handle: accountsChanged, chainChanged, and disconnect. Do it on a testnet first.

web3.js — web3.js connect MetaMask (step by step)

If you want web3 js connect MetaMask, here's a minimal flow with web3.js v1.x.

import Web3 from 'web3';

async function connectWithWeb3() {
  if (!window.ethereum) throw new Error('No injected provider');
  const web3 = new Web3(window.ethereum);
  try {
    // ask user to connect
    await window.ethereum.request({ method: 'eth_requestAccounts' });
    const accounts = await web3.eth.getAccounts();
    return { web3, account: accounts[0] };
  } catch (err) {
    throw err; // user rejected or other error
  }
}

Listen for changes:

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

ethers.js — connect and sign

Many prefer ethers for its smaller API surface and utility helpers. For react connect MetaMask or plain JS this pattern is common.

import { ethers } from 'ethers';

async function connectWithEthers() {
  if (!window.ethereum) throw new Error('No provider');
  const provider = new ethers.providers.Web3Provider(window.ethereum, 'any');
  await provider.send('eth_requestAccounts', []);
  const signer = provider.getSigner();
  const address = await signer.getAddress();
  return { provider, signer, address };
}

Signing a message (useful for auth):

const signature = await signer.signMessage('Login request: 167244');

Server-side verification is a standard pattern (see below).

React integrations (react connect MetaMask)

A simple React button that asks MetaMask to connect is the most common UX. Below is the simplest pattern (no external hooks):

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

export default function ConnectButton() {
  const [addr, setAddr] = useState(null);

  async function connect() {
    try {
      const { signer } = await connectWithEthers();
      setAddr(await signer.getAddress());
    } catch (e) {
      console.error(e);
    }
  }

  return <button onClick={connect}>{addr || 'Connect MetaMask'}</button>;
}

If you prefer structured tooling, compare connect-web3-react, web3modal connect MetaMask, or wagmi connect MetaMask for pre-built hooks and modal flows. React Native cannot directly access the browser extension — use WalletConnect or deep linking to the mobile app instead (see walletconnect-guide).

Server patterns: Node.js and Python (nodejs connect to MetaMask / python connect MetaMask)

Node.js cannot directly talk to a browser extension. So how do servers "connect to MetaMask"? The common pattern is:

  1. Frontend asks MetaMask to sign a challenge message.
  2. Frontend sends message + signature to backend.
  3. Backend verifies the signature and maps it to an address.

Node verification (ethers):

const { ethers } = require('ethers');
function verify(message, signature) {
  return ethers.utils.verifyMessage(message, signature); // returns address
}

Python verification (web3.py / eth-account):

from eth_account.messages import encode_defunct
from eth_account import Account

def verify(message, signature):
    msg = encode_defunct(text=message)
    return Account.recover_message(msg, signature=signature)

For on-chain reads/writes from a server you use an RPC node (Infura, Alchemy, or self-hosted). But that is separate from MetaMask — which remains a client signer.

Tooling & middlewares: web3modal, WalletConnect, wagmi

Tooling can simplify UX. web3modal connect MetaMask provides a modal picker for many wallets. WalletConnect links mobile wallets (useful for react native connect MetaMask flows). wagmi exposes React hooks for providers and signers.

Table: quick comparison

Integration Best for Notes
web3.js Classic dApps Familiar API, larger bundle
ethers.js Modern JS apps Utilities for signatures and ABI parsing
web3modal / WalletConnect Multi-wallet UX Adds mobile support (deep links)
wagmi / web3-react React apps Hook-based state management

For local development see connect-ganache-local and connect-remix.

Security checklist for dApp developers

  • Always show the user the exact message they sign (no hidden payloads).
  • Handle chain and account changes (accountsChanged / chainChanged).
  • Limit token approvals in your UI and explain what an "infinite allowance" means. I once approved an unlimited allowance — and then had to revoke it; don't learn that the hard way.
  • Verify signatures on the server (don’t assume the browser is honest).
  • Offer a testnet / dry-run flow (send small amounts first).

For UI actions around approvals, link to token-approvals-revoke and security-best-practices.

Troubleshooting quick hits

  • No provider? Ask users to install the extension: install-metamask-extension.
  • Wrong chain? Prompt with wallet_switchEthereumChain (handle add via wallet_addEthereumChain if needed) — see add-custom-network.
  • Connection flickers or returns null on refresh? Re-initialize provider on load and resubscribe to events.

For common UI problems see connect-button-troubleshoot and troubleshooting-dapp-connections.

Conclusion & next steps

Connecting to MetaMask is straightforward once you accept two realities: the provider lives in the browser, and signing belongs to the user. Which library you choose (web3.js, ethers, or a React toolkit) depends on your stack and team preferences.

Try these steps locally: wire up a simple connect button, request a signature, and verify it on a small Node or Python endpoint. And if you want to expand later, check the guides for connecting to networks (connect-to-networks, add-custom-network) and mobile flows (connect-walletconnect).

If you'd like, follow the hands-on examples here and then test against a local chain — it's the safest way to learn. Happy building.

Try Tangem secure wallet →