Set up your development environment
You can easily set up a simple dapp to integrate with MetaMask. For a full end-to-end tutorial using Vite, see the Create a simple React dapp tutorial.
Prerequisites
MetaMask installed in the browser of your choice on your development machine. We recommend running a development network on MetaMask when developing a dapp.
A text editor of your choice, such as VS Code. You can install the Live Server extension for VS Code to easily launch a local development server for your dapp.
A module bundler, such as Webpack.
npm.
Set up a new project
Create a project directory with the following structure:
simple-dapp/
├─ src/
│ ├─ index.js
├─ dist/
│ ├─ index.html
For any Ethereum dapp to work, your project script index.js
must:
- Detect the Ethereum provider.
- Detect which Ethereum network the user is connected to.
- Access the user's Ethereum accounts.
If you import any modules into your project, such as
@metamask/detect-provider
, use a bundler such as
Webpack to compile the modules and create an output script
dist/main.js
.
See Webpack's Getting Started guide for more information.
We also recommend setting up MetaMask SDK to enable a reliable, secure, and seamless connection from your dapp to the MetaMask browser extension and MetaMask Mobile.
Example
The following is an example simple dapp script and HTML file:
- JavaScript
- HTML
/*****************************************/
/* Detect the MetaMask Ethereum provider */
/*****************************************/
import detectEthereumProvider from '@metamask/detect-provider';
const provider = await detectEthereumProvider();
if (provider) {
startApp(provider);
} else {
console.log('Please install MetaMask!');
}
function startApp(provider) {
if (provider !== window.ethereum) {
console.error('Do you have multiple wallets installed?');
}
}
/**********************************************************/
/* Handle chain (network) and chainChanged (per EIP-1193) */
/**********************************************************/
const chainId = await window.ethereum.request({ method: 'eth_chainId' });
window.ethereum.on('chainChanged', handleChainChanged);
function handleChainChanged(chainId) {
window.location.reload();
}
/***********************************************************/
/* Handle user accounts and accountsChanged (per EIP-1193) */
/***********************************************************/
let currentAccount = null;
window.ethereum.request({ method: 'eth_accounts' })
.then(handleAccountsChanged)
.catch((err) => {
console.error(err);
});
window.ethereum.on('accountsChanged', handleAccountsChanged);
function handleAccountsChanged(accounts) {
if (accounts.length === 0) {
console.log('Please connect to MetaMask.');
} else if (accounts[0] !== currentAccount) {
currentAccount = accounts[0];
showAccount.innerHTML = currentAccount;
}
}
/*********************************************/
/* Access the user's accounts (per EIP-1102) */
/*********************************************/
const ethereumButton = document.querySelector('.enableEthereumButton');
const showAccount = document.querySelector('.showAccount');
ethereumButton.addEventListener('click', () => {
getAccount();
});
async function getAccount() {
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' })
.catch((err) => {
if (err.code === 4001) {
console.log('Please connect to MetaMask.');
} else {
console.error(err);
}
});
const account = accounts[0];
showAccount.innerHTML = account;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Simple dapp</title>
<script type="module" src="main.js"></script>
</head>
<body>
<!-- Display a connect button and the current account -->
<button class="enableEthereumButton">Enable Ethereum</button>
<h2>Account: <span class="showAccount"></span></h2>
</body>
</html>