Developer Guide: Detecting & Integrating MetaMask in dApps

Get the Best Crypto Wallet — Start Now

Developer Guide: Detecting & Integrating MetaMask in dApps

Table of contents

Quick overview

This guide shows practical, code-first ways to detect MetaMask in browser-based dApps, react to account events, read accounts via Web3.js, and deploy contracts from the browser. It focuses on measurable behaviors (which RPC call returns what, which events fire, and how to estimate gas) rather than vague recommendations.

I use these patterns when building test deployments and front-end integrations. When I first set this up I accidentally pushed a deploy with too-low gas; that taught me to always estimateGas and test on a testnet first. But once you get the flow right, integration becomes routine.

If you need setup help, check install-metamask-chrome-extension or metamask-mobile-ios-android.

How to detect if MetaMask is connected in React

Goal: detect presence of an injected provider and whether the user has exposed accounts to your site. The common pattern checks for window.ethereum, then calls eth_accounts (no modal) or eth_requestAccounts (prompts).

React functional example with Web3.js:

import React, { useEffect, useState } from 'react';
import Web3 from 'web3';

export default function WalletStatus() {
  const [account, setAccount] = useState(null);
  const [connected, setConnected] = useState(false);

  useEffect(() => {
    if (!window.ethereum) return;
    const web3 = new Web3(window.ethereum);

    // Non-invasive check: won't popup UI
    window.ethereum.request({ method: 'eth_accounts' })
      .then(accounts => {
        setConnected(accounts && accounts.length > 0);
        setAccount(accounts[0] || null);
      })
      .catch(console.error);

  }, []);

  return (<div>Connected: {String(connected)} — Account: {account || 'none'}</div>);
}

Tip: Call eth_requestAccounts only in response to a user action (button click). Prompting automatically harms conversion.

Listen for account and chain changes (detect MetaMask account change)

You must assume the user can switch accounts or networks at any time. Which listeners matter? accountsChanged and chainChanged (EIP-1193).

Example listener setup (clean up listeners on unmount):

if (window.ethereum && window.ethereum.on) {
  const onAccountsChanged = (accounts) => { /* update UI/state */ };
  const onChainChanged = (chainId) => { /* update provider, refresh state */ };

  window.ethereum.on('accountsChanged', onAccountsChanged);
  window.ethereum.on('chainChanged', onChainChanged);

  // remove listeners when done
  window.ethereum.removeListener('accountsChanged', onAccountsChanged);
  window.ethereum.removeListener('chainChanged', onChainChanged);
}

How fast do events fire? In practice account changes are immediate (sub-200ms) inside the tab. But if the user switches networks in the wallet UI, your app should re-validate chainId before signing transactions.

Get MetaMask account from web3

If you asked search engines "get metamask account from web3" the canonical answer is web3.eth.getAccounts() or window.ethereum.request({ method: 'eth_accounts' }). Example:

const web3 = new Web3(window.ethereum);
const accounts = await web3.eth.getAccounts();
const primary = accounts[0];

Remember: an empty array means either locked wallet or no permission granted. Use eth_requestAccounts to prompt.

Deploy contracts with MetaMask (deploy contract with MetaMask)

Deploying from the browser is a standard flow: create a deploy transaction whose data = contract bytecode (+ ctor args). Below are tested patterns.

Web3.js (deploy smart contract MetaMask and bytecode):

const web3 = new Web3(window.ethereum);
const accs = await web3.eth.getAccounts();
const deploy = new web3.eth.Contract(abi).deploy({ data: bytecode, arguments: [/* args */] });
const gas = await deploy.estimateGas({ from: accs[0] });
const instance = await deploy.send({ from: accs[0], gas });
console.log('address', instance.options.address);

Ethers.js (ContractFactory + signer):

const provider = new ethers.providers.Web3Provider(window.ethereum);
await provider.send('eth_requestAccounts', []);
const signer = provider.getSigner();
const factory = new ethers.ContractFactory(abi, bytecode, signer);
const contract = await factory.deploy(...args);
await contract.deployed();
console.log(contract.address);

If you prefer manual low-level deployment, send a transaction with the bytecode in the data field. And when you want to test quick function calls without a UI, Etherscan's write UI will connect to your wallet (etherscan write contract metamask flows connect via "Connect to Web3"). See using-etherscan-with-metamask.

Expose accounts, WebSockets, and WalletConnect

Can you expose your device accounts through websocket MetaMask? Not directly—MetaMask injects an in-page provider, it does not expose a remote WebSocket endpoint for your app to connect to over the network. If you need remote connectivity, common options:

Method WebSocket support UX cost Notes
Injected provider (window.ethereum) No Low Best for same-tab dApps; fast event hooks
WalletConnect (bridge) Yes (bridge; v2 supports WebSocket) Medium Good for mobile-to-desktop flows — see connect-to-dapps-walletconnect
Custom relay / proxy Yes High Requires local agent or server—higher security burden

So if you searched to "expose your device accounts through websocket MetaMask" the practical answer is: use WalletConnect or implement a secure relay; do not attempt to expose private keys.

Debugging tips and common pitfalls

For detailed transaction troubleshooting see transaction-errors-and-fixes.

Security and UX best practices for dApps

But always avoid asking for seed phrases. Ever.

Who this is for — and who should look elsewhere

This integration guide is aimed at front-end engineers building EVM-compatible dApps that will interact with browser or in-app wallets, perform swaps, staking calls, or deploy contracts from the UI.

Look elsewhere if you require server-side signing, fully headless wallet management, or a strictly hardware-wallet-only UX (see ledger-with-metamask-guide).

FAQ

Q: Is it safe to keep crypto in a hot wallet? A: Hot wallets trade security for convenience. For frequent DeFi use they’re practical for small balances; for long-term storage or large sums consider hardware wallets.

Q: How do I revoke token approvals? A: Use the revoke flows or tools covered on token-allowances-and-revoke.

Q: What happens if I lose my phone? A: Restore from your seed phrase (see seed-phrase-backup-recovery).

Q: How do I detect MetaMask account change? A: Listen for the provider event 'accountsChanged' (example above).

Q: How do I get MetaMask account from web3? A: Call web3.eth.getAccounts(), or window.ethereum.request({ method: 'eth_accounts' }). If result is empty prompt via eth_requestAccounts.

Conclusion and next steps

This guide shows the concrete steps to detect and integrate MetaMask in React dApps, track account and chain changes, read accounts with Web3.js, and deploy contracts from the browser (deploy contract with MetaMask). If you want deployment-focused next steps, read developer-rpc-and-node-guide and contract-interactions. Build a small test dApp, sign a transaction, and deploy to a testnet — that will validate your flow quickly.

Get the Best Crypto Wallet — Start Now