
Description
Set up a React or React Native app with wagmi and a MetaMask connector implementation that matches your installed @metamask/connect-evm version. Use when integrating MetaMask with wagmi, configuring the metaMask() connector, or building connect/sign/send flows.
SKILL.md
Setup wagmi App with MetaMask
When to use
Use this skill when:
- Integrating MetaMask with wagmi in a React or React Native app
- Configuring the
metaMask()wagmi connector for@metamask/connect-evm - Building connect, sign message, send transaction, or switch chain flows
- Debugging wagmi + MetaMask connector issues
Workflow
Step 1: Install dependencies
npm install wagmi @tanstack/react-query viem
# Install the @metamask/connect-evm version wagmi declares as its peer range
# (check with: npm info @wagmi/connectors peerDependencies — currently ^1.3.0):
npm install @metamask/connect-evm@"^1.3.0"
The connect-evm-backed metaMask() connector requires wagmi >= 3.6 / @wagmi/connectors >= 8. Match @metamask/connect-evm to wagmi's declared optional peer range rather than installing "latest" — the current 2.x line does not satisfy ^1.3.0. @metamask/connect-multichain is installed transitively; you do not need to add it.
Step 2: Create wagmi config (browser)
import { createConfig, http } from 'wagmi';
import { mainnet, sepolia, optimism, celo } from 'wagmi/chains';
// Requires wagmi >= 3.6 / @wagmi/connectors >= 8. On older wagmi, copy the
// reference connector from connect-monorepo/integrations/wagmi/metamask-connector.ts
import { metaMask } from 'wagmi/connectors';
export const wagmiConfig = createConfig({
chains: [mainnet, sepolia, optimism, celo],
connectors: [
metaMask({
dapp: {
name: 'My DApp',
url: typeof window !== 'undefined' ? window.location.href : undefined,
iconUrl: undefined, // optional
},
mobile: {
preferredOpenLink: undefined, // React Native: (deeplink) => Linking.openURL(deeplink)
useDeeplink: undefined,
},
connectAndSign: undefined, // optional
connectWith: undefined, // optional { method, params }
debug: false,
}),
],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
[optimism.id]: http(),
[celo.id]: http(),
},
});
Connector parameters (metaMask(parameters?)):
| Parameter | Type | Description |
|---|---|---|
dapp | { name, url?, iconUrl? } | DApp metadata. Deprecated: dappMetadata maps to dapp |
mobile.preferredOpenLink | (deeplink) => void | RN: (deeplink) => Linking.openURL(deeplink) |
mobile.useDeeplink | boolean | Use deeplink for mobile |
connectAndSign | string | Optional |
connectWith | { method, params } | Optional |
debug | boolean | Enable debug logs |
Connector id: 'metaMaskSDK', name: 'MetaMask'
Step 3: Provider hierarchy (React)
import { WagmiProvider } from 'wagmi';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
gcTime: 1_000 * 60 * 60 * 24,
networkMode: 'offlineFirst',
refetchOnWindowFocus: false,
retry: 0,
},
mutations: { networkMode: 'offlineFirst' },
},
});
<WagmiProvider config={wagmiConfig}>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</WagmiProvider>
Step 4: React Native polyfills (before wagmi config)
Import polyfills before any wagmi/viem imports:
// At the very top of your entry file (e.g. index.js or App.tsx)
import 'react-native-get-random-values';
import { Buffer } from 'buffer';
global.Buffer = Buffer;
// window, Event, CustomEvent, metro shims as per RN skills
Add to metaMask() params:
import { Linking } from 'react-native';
metaMask({
dapp: { name: 'My DApp', url: '...' },
mobile: {
preferredOpenLink: (deeplink) => Linking.openURL(deeplink),
},
})
Use createAsyncStoragePersister with AsyncStorage instead of localStorage for persistence.
Step 5: Full component example
import { useConnection, useBalance, useConnect, useConnectors, useDisconnect, useSendTransaction, useSignMessage, useSwitchChain, useChains, useChainId } from 'wagmi';
import { parseEther, formatEther } from 'viem';
function WalletDemo() {
const { address, isConnected, status } = useConnection();
const { data: balance } = useBalance({ address });
const { mutateAsync: connectAsync, status: connectStatus } = useConnect();
const connectors = useConnectors();
const { disconnect } = useDisconnect();
const { sendTransactionAsync } = useSendTransaction();
const { signMessageAsync } = useSignMessage();
const { switchChainAsync } = useSwitchChain();
const chains = useChains();
const chainId = useChainId();
const metaMaskConnector = connectors.find((c) => c.id === 'metaMaskSDK');
const handleConnect = async () => {
if (!metaMaskConnector) return;
await connectAsync({ connector: metaMaskConnector, chainId: 1 });
};
const handleSignMessage = async () => {
const sig = await signMessageAsync({ message: 'Hello MetaMask!' });
console.log(sig);
};
const handleSendTx = async () => {
const hash = await sendTransactionAsync({
to: '0x...' as `0x${string}`,
value: parseEther('0.001'),
});
console.log(hash);
};
const handleSwitchChain = async (id: number) => {
await switchChainAsync({ chainId: id });
};
if (!isConnected) {
return (
<button onClick={handleConnect} disabled={connectStatus === 'pending'}>
Connect MetaMask
</button>
);
}
return (
<div>
<p>Address: {address}</p>
<p>Balance: {balance ? formatEther(balance.value) : '—'} ETH</p>
<p>Chain: {chainId}</p>
<button onClick={handleSignMessage}>Sign Message</button>
<button onClick={handleSendTx}>Send 0.001 ETH</button>
<select value={chainId} onChange={(e) => handleSwitchChain(Number(e.target.value))}>
{chains.map((c) => (
<option key={c.id} value={c.id}>
{c.name}
</option>
))}
</select>
<button onClick={() => disconnect()}>Disconnect</button>
</div>
);
}
Wagmi hooks used (v3):
useConnection:address,isConnected,status(renamed fromuseAccountin v3)useBalance: balance for connected accountuseConnect:mutateAsync(renamed fromconnectAsync),statususeConnectors: standalone hook for connector list (removed fromuseConnectin v3)useDisconnect:disconnectuseSendTransaction: send ETHuseSignMessage: sign messagesuseSwitchChain:switchChainAsyncuseChains: standalone hook for chain list (removed fromuseSwitchChainin v3)useWaitForTransactionReceipt: tx confirmationuseChainId: current chainuseBlockNumber: current block (watch: true)
Step 6: Connect flow
// wagmi v3: connectors come from useConnectors(), and useConnect() exposes
// mutateAsync (connectAsync was the v2 name)
const connectors = useConnectors();
const { mutateAsync: connect } = useConnect();
const metaMaskConnector = connectors.find((c) => c.id === 'metaMaskSDK');
await connect({ connector: metaMaskConnector, chainId: 1 });
Step 7: Error handling
Handle common errors:
UserRejectedRequestError(code 4001)ResourceUnavailableRpcError(code -32002)SwitchChainError,ChainNotConfiguredError
Important Notes
- Connector ID is
'metaMaskSDK'— always find it withconnectors.find((c) => c.id === 'metaMaskSDK'). - Wagmi disconnect is separate from multichain disconnect — disconnecting one does not disconnect the other.
- CRA/Expo import restriction: Cannot import from outside
src/— the connector may need to be copied locally. isAuthorizedretries on mobile: The connector wrapsgetAccounts()inwithTimeout(10ms per attempt) andwithRetry(3 attempts, ~11ms delay between) because the MetaMask mobile provider sometimes doesn't resolve JSON-RPC requests immediately on page load. It returnsfalseon failure (does NOT throw) — it resolves in tens of milliseconds, not seconds.- Chains in
wagmiConfigmust match chains you use — wagmi validates against configured chains. - React Native: Import polyfills before wagmi config; add
mobile.preferredOpenLink; usecreateAsyncStoragePersisterwith AsyncStorage. Polyfill requirements:react-native-get-random-valuesfirst (required for RN < 0.72), thenwindowshim (required by connect-multichain for platform detection), thenEvent/CustomEventshims (wagmi-specific — wagmi dispatches DOM events; not needed for standalone connect-* usage). Buffer is self-polyfilled by connect-multichain; keepglobal.Buffer = Bufferas a safety net for peer deps.
More skills from the metamask-connect-cursor-plugin repository
View all 18 skillsmigrate-from-sdk
migrate MetaMask SDK to connect-evm and connect-solana
Jul 13EthereumMigrationSDKWeb3migrate-wagmi-metamask-connector
migrate wagmi apps to MetaMask connector
Jul 13EthereumMigrationwagmiWeb3send-evm-transaction
send EVM transactions with MetaMask
Jul 13API DevelopmentEthereumWeb3send-solana-transaction
send Solana transactions via MetaMask
Jul 13PaymentsSolanaWeb3setup-evm-browser-app
scaffold EVM browser applications with MetaMask
Jul 13EthereumFrontendJavaScriptTypeScript +1setup-evm-react-app
scaffold React apps with MetaMask integration
Jul 13API DevelopmentEthereumFrontendReact +1
More from MetaMask
View publishersmart-accounts-kit
build dApps with MetaMask Smart Accounts
smart-accounts-kit
Jul 13API DevelopmentAuthEthereumPermissions +1x402-payments
build x402 payment flows
smart-accounts-kit
Jul 13API DevelopmentPaymentsWeb3x402discovery
discover and use services
ocap-kernel
Jul 13API DevelopmentSearchmetamask
interact with MetaMask wallet capabilities
ocap-kernel
Jul 13API DevelopmentEthereumWeb3wallet
manage wallet balances and transactions
ocap-kernel
Jul 13EthereumPaymentsWeb3metamask-connect
integrate MetaMask into dApps
connect-monorepo
Jul 13API DevelopmentEthereumSolanawagmi +1