For AI agents: a documentation index is available at /llms.txt. A markdown version of this page is available at the same URL with .md appended (or via Accept: text/markdown).
Skip to main content

Use headless mode

By default, MetaMask Connect renders its own QR code modal when connecting to MetaMask Mobile via MetaMask Wallet Protocol (MWP). Headless mode suppresses this built-in modal so you can render your own connection UI.

Use headless mode when you want to:

  • Display a custom-styled QR code that matches your dapp's design.
  • Show the connection URI in a different format (for example, a deeplink button instead of a QR code).
  • Integrate the connection flow into an existing modal or onboarding wizard.

Prerequisites​

Follow Step 1 of the quickstart to install the EVM client.

Steps​

1. Initialize the client with headless mode​

Initialize an EVM client using createEVMClient, and set ui.headless to true:

import { createEVMClient, getInfuraRpcUrls } from '@metamask/connect-evm'

const evmClient = await createEVMClient({
dapp: {
name: 'My Dapp',
url: window.location.href,
},
api: {
supportedNetworks: {
...getInfuraRpcUrls({ infuraApiKey: '<YOUR_INFURA_API_KEY>' }),
},
},
ui: { headless: true },
})

2. Register a display_uri listener before connecting​

The display_uri event fires on the EIP-1193 provider during the connecting phase with a one-time-use pairing URI. You must register the listener before calling connect, or you may miss the event:

const provider = evmClient.getProvider()

provider.on('display_uri', uri => {
showCustomQrModal(uri)
})

As an alternative to provider.on, you can pass a displayUri callback when initializing the client via eventHandlers (see the migration guide).

3. Connect and handle the result​

Call connect to connect to MetaMask, and handle the result:

try {
await evmClient.connect({ chainIds: ['0x1'] })
hideCustomQrModal()
} catch (err) {
hideCustomQrModal()
if (err.code === 4001) {
// User rejected — show retry UI
} else {
console.error('Connection failed:', err)
}
}

Important considerations​

URI is one-time-use​

The pairing URI delivered by display_uri is a one-time-use token. Once used or expired, it cannot be reused. If the connection fails, call connect again to generate a fresh URI.

display_uri only fires during connecting​

The event fires only while the client status is 'connecting'. After the connection resolves (success or error), display_uri stops firing.

Extension connections skip QR​

When the MetaMask browser extension is installed and ui.preferExtension is true (the default), the SDK connects directly through the extension. No display_uri event fires because no QR code is needed.

To display the QR connection option even when the extension is available, set ui.preferExtension to false:

const evmClient = await createEVMClient({
dapp: { name: 'My Dapp', url: window.location.href },
api: {
supportedNetworks: {
...getInfuraRpcUrls({ infuraApiKey: '<YOUR_INFURA_API_KEY>' }),
},
},
ui: {
headless: true,
preferExtension: false,
},
})

Monitor connection status​

The EVM client exposes a status property ('loaded' | 'pending' | 'connecting' | 'connected' | 'disconnected') you can read when updating UI.

Next steps​