| name | setup-wagmi-app |
| 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. |
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
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';
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,
},
mobile: {
preferredOpenLink: undefined,
useDeeplink: undefined,
},
connectAndSign: undefined,
connectWith: undefined,
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:
import 'react-native-get-random-values';
import { Buffer } from 'buffer';
global.Buffer = Buffer;
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({ : metaMaskConnector, : });
};
= () => {
sig = ({ : });
.(sig);
};
= () => {
hash = ({
: ,
: (),
});
.(hash);
};
= () => {
({ : id });
};
(!isConnected) {
(
);
}
(
);
}
Wagmi hooks used (v3):
useConnection: address, isConnected, status (renamed from useAccount in v3)
useBalance: balance for connected account
useConnect: mutateAsync (renamed from connectAsync), status
useConnectors: standalone hook for connector list (removed from useConnect in v3)
useDisconnect: disconnect
useSendTransaction: send ETH
useSignMessage: sign messages
useSwitchChain: switchChainAsync
useChains: standalone hook for chain list (removed from useSwitchChain in v3)
useWaitForTransactionReceipt: tx confirmation
useChainId: current chain
useBlockNumber: current block (watch: true)
Step 6: Connect flow
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 with connectors.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.
isAuthorized retries on mobile: The connector wraps getAccounts() in withTimeout (10ms per attempt) and withRetry (3 attempts, ~11ms delay between) because the MetaMask mobile provider sometimes doesn't resolve JSON-RPC requests immediately on page load. It returns false on failure (does NOT throw) — it resolves in tens of milliseconds, not seconds.
- Chains in
wagmiConfig must match chains you use — wagmi validates against configured chains.
- React Native: Import polyfills before wagmi config; add
mobile.preferredOpenLink; use createAsyncStoragePersister with AsyncStorage. Polyfill requirements: react-native-get-random-values first (required for RN < 0.72), then window shim (required by connect-multichain for platform detection), then Event/CustomEvent shims (wagmi-specific — wagmi dispatches DOM events; not needed for standalone connect-* usage). Buffer is self-polyfilled by connect-multichain; keep global.Buffer = Buffer as a safety net for peer deps.