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

# React Native Metro polyfill issues

React Native uses the Metro bundler, which cannot resolve Node.js built-in modules. MetaMask Connect packages and their dependencies reference modules like `stream`, `crypto`, `buffer`, and `http`. Some code paths expect a browser-like `window` object, which React Native does not provide. MetaMask Connect uses `eventemitter3` internally and does not require DOM `Event` or `CustomEvent` globals; if you use **wagmi**, you may need to polyfill those separately.

This guide walks through the required polyfills, Metro configuration, and related React Native setup (including deeplinks to MetaMask Mobile).

Expo-managed workflow

Polyfilling is not supported with the "Expo Go" app. It is compatible only with Custom Dev Client and [Expo Application Services (EAS)](https://docs.expo.dev/eas/) builds. [Prebuild your Expo app](https://docs.expo.dev/workflow/continuous-native-generation/#usage) to generate native code before proceeding.

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

### 1. Install required packages[​](#1-install-required-packages "Direct link to 1. Install required packages")

- npm
- Yarn
- pnpm
- Bun

```
npm install react-native-get-random-values buffer readable-stream @react-native-async-storage/async-storage

```

```
yarn add react-native-get-random-values buffer readable-stream @react-native-async-storage/async-storage

```

```
pnpm add react-native-get-random-values buffer readable-stream @react-native-async-storage/async-storage

```

```
bun add react-native-get-random-values buffer readable-stream @react-native-async-storage/async-storage

```

`react-native-get-random-values` provides `crypto.getRandomValues`, which MetaMask Connect requires. `readable-stream` provides a `stream` shim for Metro. `buffer` provides the `Buffer` global. `@react-native-async-storage/async-storage` is needed for session persistence.

### 2. Configure Metro[​](#2-configure-metro "Direct link to 2. Configure Metro")

Map Node.js built-in modules to React Native-compatible shims or an empty module stub. Create an empty module file first:

src/empty-module.js

```
module.exports = {}

```

Then update your Metro config:

- Bare React Native
- Expo

metro.config.js

```
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config')
const path = require('path')

const emptyModule = path.resolve(__dirname, 'src/empty-module.js')

const config = {
  resolver: {
    extraNodeModules: {
      stream: require.resolve('readable-stream'),
      crypto: emptyModule,
      http: emptyModule,
      https: emptyModule,
      net: emptyModule,
      tls: emptyModule,
      zlib: emptyModule,
      os: emptyModule,
      dns: emptyModule,
      assert: emptyModule,
      url: emptyModule,
      path: emptyModule,
      fs: emptyModule,
    },
  },
}

module.exports = mergeConfig(getDefaultConfig(__dirname), config)

```

metro.config.js

```
const { getDefaultConfig } = require('expo/metro-config')
const path = require('path')

const config = getDefaultConfig(__dirname)
const emptyModule = path.resolve(__dirname, 'src/empty-module.js')

config.resolver.extraNodeModules = {
  stream: require.resolve('readable-stream'),
  crypto: emptyModule,
  http: emptyModule,
  https: emptyModule,
  net: emptyModule,
  tls: emptyModule,
  zlib: emptyModule,
  os: emptyModule,
  dns: emptyModule,
  assert: emptyModule,
  url: emptyModule,
  path: emptyModule,
  fs: emptyModule,
}

module.exports = config

```

### 3. Create the polyfills file[​](#3-create-the-polyfills-file "Direct link to 3. Create the polyfills file")

Create `polyfills.ts` at the project root (or `src/polyfills.ts`) with the following global shims.

#### Base polyfills (MetaMask Connect)[​](#base-polyfills-metamask-connect "Direct link to Base polyfills (MetaMask Connect)")

These cover `Buffer`, `crypto.getRandomValues` (via your entry import), and a minimal `window` shim:

polyfills.ts

```
import { Buffer } from 'buffer'

global.Buffer = Buffer

// Polyfill window — React Native doesn't have a browser window object
let windowObj: any
if (typeof global !== 'undefined' && global.window) {
  windowObj = global.window
} else if (typeof window !== 'undefined') {
  windowObj = window
} else {
  windowObj = {}
}

if (!windowObj.location) {
  windowObj.location = {
    hostname: 'mydapp.com',
    href: 'https://mydapp.com',
  }
}
if (typeof windowObj.addEventListener !== 'function') {
  windowObj.addEventListener = () => {}
}
if (typeof windowObj.removeEventListener !== 'function') {
  windowObj.removeEventListener = () => {}
}
if (typeof windowObj.dispatchEvent !== 'function') {
  windowObj.dispatchEvent = () => true
}

if (typeof global !== 'undefined') {
  global.window = windowObj
}

```

#### Optional wagmi polyfills for Event and CustomEvent[​](#optional-wagmi-polyfills-for-event-and-customevent "Direct link to Optional wagmi polyfills for Event and CustomEvent")

If you use **wagmi**, add the following to `polyfills.ts` after the `window` shim (React Native does not provide DOM `Event` or `CustomEvent`, which wagmi-related code may expect):

```
// Polyfill Event if missing
if (typeof global.Event === 'undefined') {
  class EventPolyfill {
    type: string
    bubbles: boolean
    cancelable: boolean
    defaultPrevented = false
    constructor(type: string, options?: EventInit) {
      this.type = type
      this.bubbles = options?.bubbles ?? false
      this.cancelable = options?.cancelable ?? false
    }
    preventDefault() {
      this.defaultPrevented = true
    }
    stopPropagation() {}
    stopImmediatePropagation() {}
  }
  global.Event = EventPolyfill as any
  windowObj.Event = EventPolyfill as any
}

// Polyfill CustomEvent if missing
if (typeof global.CustomEvent === 'undefined') {
  const EventClass =
    global.Event ||
    class {
      type: string
      constructor(type: string) {
        this.type = type
      }
    }
  class CustomEventPolyfill extends (EventClass as any) {
    detail: any
    constructor(type: string, options?: CustomEventInit) {
      super(type, options)
      this.detail = options?.detail ?? null
    }
  }
  global.CustomEvent = CustomEventPolyfill as any
  windowObj.CustomEvent = CustomEventPolyfill as any
}

```

## Step 4: Set up the entry file import order[​](#step-4-set-up-the-entry-file-import-order "Direct link to Step 4: Set up the entry file import order")

The import order is **critical**. `react-native-get-random-values` must be the very first import, followed by the polyfills file, before any SDK or application code:

- Bare React Native
- Expo Router

index.js

```
import 'react-native-get-random-values' // Must be first
import './polyfills' // Must be second

import { AppRegistry } from 'react-native'
import App from './App'
import { name as appName } from './app.json'

AppRegistry.registerComponent(appName, () => App)

```

app/_layout.tsx

```
import 'react-native-get-random-values' // Must be first
import '../polyfills' // Must be second

// ... rest of layout

```

## Deeplinks not opening MetaMask app[​](#deeplinks-not-opening-metamask-app "Direct link to Deeplinks not opening MetaMask app")

If MetaMask Mobile does not open when your dapp initiates a connection, the `mobile.preferredOpenLink` callback is probably not set. Pass a function that calls `Linking.openURL`:

```
import { Linking } from 'react-native'

const client = await createEVMClient({
  dapp: { name: 'My Dapp', url: 'https://mydapp.com' },
  mobile: {
    preferredOpenLink: deeplink => Linking.openURL(deeplink),
  },
})

```

Use the same `mobile.preferredOpenLink` pattern with [createMultichainClient](/metamask-connect/multichain/reference/methods/) or [createSolanaClient](/metamask-connect/solana/reference/methods/) when you initialize those clients in React Native.

## Common errors and solutions[​](#common-errors-and-solutions "Direct link to Common errors and solutions")

### `crypto.getRandomValues is not a function`[​](#cryptogetrandomvalues-is-not-a-function "Direct link to cryptogetrandomvalues-is-not-a-function")

**Cause**: `react-native-get-random-values` was not imported before MetaMask Connect.

**Fix**: Ensure `import 'react-native-get-random-values'` is the very first import in your entry file, before any MetaMask Connect imports or polyfills.

### `Buffer is not defined`[​](#buffer-is-not-defined "Direct link to buffer-is-not-defined")

**Cause**: The `Buffer` polyfill was not loaded before something in the bundle accessed `Buffer`, or a peer dependency (for example `eciesjs`) ran before MetaMask Connect’s React Native entry could apply its own `Buffer` shim.

**Fix**: Set `global.Buffer = Buffer` in your polyfills file, and import that file immediately after `react-native-get-random-values` so the global is defined before other imports run.

### `Cannot resolve module 'stream'` (or `crypto`, `http`, etc.)[​](#cannot-resolve-module-stream-or-crypto-http-etc "Direct link to cannot-resolve-module-stream-or-crypto-http-etc")

**Cause**: Metro does not know how to resolve Node.js built-in modules.

**Fix**: Add `extraNodeModules` to your `metro.config.js` as shown in [Step 2](#2-configure-metro). Map `stream` to `readable-stream` and stub the rest with the empty module.

### `Event is not defined` or `CustomEvent is not defined`[​](#event-is-not-defined-or-customevent-is-not-defined "Direct link to event-is-not-defined-or-customevent-is-not-defined")

**Cause**: React Native does not provide browser `Event` or `CustomEvent` classes. This typically appears when using **wagmi** (or another dependency that expects DOM events). MetaMask Connect uses `eventemitter3` internally and does not require these globals.

**Fix**: If you use wagmi, append the `Event` and `CustomEvent` polyfills from [Optional wagmi polyfills for Event and CustomEvent](#optional-wagmi-polyfills-for-event-and-customevent) to your `polyfills.ts` after the base `window` shim.

### Expo Go not working[​](#expo-go-not-working "Direct link to Expo Go not working")

**Cause**: Polyfilling native modules is not supported with Expo Go.

**Fix**: Use a [Custom Dev Client](https://docs.expo.dev/develop/development-builds/introduction/) or [EAS builds](https://docs.expo.dev/eas/). Run `npx expo prebuild` before building.

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

- See the [troubleshooting overview](/metamask-connect/troubleshooting/).
- Follow the [EVM React Native quickstart](/metamask-connect/evm/quickstart/react-native/).
- Follow the [Solana React Native quickstart](/metamask-connect/solana/quickstart/react-native/).
