> For the complete documentation index, see [llms.txt](/llms.txt).

# 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[​](#prerequisites "Direct link to Prerequisites")

Follow Step 1 of the [quickstart](/metamask-connect/multichain/quickstart/javascript/) to install the multichain client.

## Steps[​](#steps "Direct link to Steps")

### 1. Initialize the client with headless mode[​](#1-initialize-the-client-with-headless-mode "Direct link to 1. Initialize the client with headless mode")

Initialize a multichain client using [createMultichainClient](/metamask-connect/multichain/reference/methods/#createmultichainclient), and set `ui.headless` to `true`:

```
import { createMultichainClient, getInfuraRpcUrls } from '@metamask/connect-multichain'

const client = await createMultichainClient({
  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[​](#2-register-a-display%5Furi-listener-before-connecting "Direct link to 2-register-a-display_uri-listener-before-connecting")

The `display_uri` event fires 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:

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

```

### 3. Connect and handle the result[​](#3-connect-and-handle-the-result "Direct link to 3. Connect and handle the result")

Call [connect](/metamask-connect/multichain/reference/methods/#connect) to connect to MetaMask, and handle the result:

```
try {
  await client.connect(['eip155:1', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'], [])
  hideCustomQrModal()
} catch (err) {
  hideCustomQrModal()
  if (err.code === 4001) {
    // User rejected — show retry UI
  } else {
    console.error('Connection failed:', err)
  }
}

```

## Important considerations[​](#important-considerations "Direct link to Important considerations")

### URI is one-time-use[​](#uri-is-one-time-use "Direct link to 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[​](#display%5Furi-only-fires-during-connecting "Direct link to 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[​](#extension-connections-skip-qr "Direct link to 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 client = await createMultichainClient({
  dapp: { name: 'My Dapp', url: window.location.href },
  ui: {
    headless: true,
    preferExtension: false,
  },
})

```

### Monitor connection status[​](#monitor-connection-status "Direct link to Monitor connection status")

Use the [stateChanged](/metamask-connect/multichain/reference/methods/#events) event to track the connection lifecycle and update your UI accordingly:

```
client.on('stateChanged', status => {
  // status: 'loaded' | 'pending' | 'connecting' | 'connected' | 'disconnected'
  switch (status) {
    case 'connecting':
      showLoadingIndicator()
      break
    case 'connected':
      hideCustomQrModal()
      showConnectedUI()
      break
    case 'disconnected':
      showDisconnectedUI()
      break
  }
})

```

## Next steps[​](#next-steps "Direct link to Next steps")

- [Send transactions on EVM and Solana](/metamask-connect/multichain/guides/send-transactions/) using `invokeMethod`.
- [Sign messages on EVM and Solana](/metamask-connect/multichain/guides/sign-transactions/) using `invokeMethod`.
- See the [Multichain methods reference](/metamask-connect/multichain/reference/methods/) for the full API.
