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

# Bundler polyfill issues - React Native Metro

This page documents how to configure Metro to handle Node.js polyfills required by the Embedded Wallets React Native SDK.

## v10 (recommended)

SDK v10 ships a Metro helper and a setup module that configure the polyfills for you. You don't need manual `extraNodeModules`.

### 1. Install the SDK

**npm**

```bash
npm install @web3auth/react-native-sdk
npm install --save-dev @babel/plugin-transform-export-namespace-from

```

**Yarn**

```bash
yarn add @web3auth/react-native-sdk
yarn add --dev @babel/plugin-transform-export-namespace-from

```

**pnpm**

```bash
pnpm add @web3auth/react-native-sdk
pnpm add --save-dev @babel/plugin-transform-export-namespace-from

```

**Bun**

```bash
bun add @web3auth/react-native-sdk
bun add --dev @babel/plugin-transform-export-namespace-from

```

Also install `react-native-quick-crypto`: 1.x (with `react-native-nitro-modules` and `react-native-quick-base64` 3.x or later) for React Native 0.75 or later with the New Architecture, or 0.7.x for earlier versions. See [Install](/embedded-wallets/sdk/react-native/#1-install-the-sdk-and-its-peer-packages). The setup module installs it as the global `crypto` object. See [Common errors](#common-errors) if it's missing.

### 2. Entry point, add setup import

Add `import "@web3auth/react-native-sdk/setup"` as the **first line** of your app's entry file:

**Bare React Native**

index.js

```js
import '@web3auth/react-native-sdk/setup' // must be first
import { AppRegistry } from 'react-native'
import App from './App'
import { name as appName } from './app.json'
AppRegistry.registerComponent(appName, () => App)

```

**Expo**

Create `index.ts` and set `"main": "index.ts"` in `package.json`:

index.ts

```ts
import '@web3auth/react-native-sdk/setup' // must be first
import { registerRootComponent } from 'expo'
import App from './App'
registerRootComponent(App)

```

### 3. Update `metro.config.js`

**Bare React Native**

metro.config.js

```js
const { getDefaultConfig } = require('@react-native/metro-config')
const { withWeb3Auth } = require('@web3auth/react-native-sdk/metro-config')

const config = getDefaultConfig(__dirname)
module.exports = withWeb3Auth(config)

```

**Expo**

metro.config.js

```js
const { getDefaultConfig } = require('expo/metro-config')
const { withWeb3Auth } = require('@web3auth/react-native-sdk/metro-config')

const config = getDefaultConfig(__dirname)
module.exports = withWeb3Auth(config)

```

info

Polyfills are not supported with the Expo Go app. Use a custom dev client or an EAS build and run `npx expo prebuild` to generate native code.

`withWeb3Auth` applies all required polyfill aliases (`crypto`, `stream`, `buffer`, and so on) internally. You can still add custom `sourceExts` or other resolver options after calling it.

### 4. Update `babel.config.js`

Add `@babel/plugin-transform-export-namespace-from`. Without it, Metro fails to parse some SDK internal re-exports.

**Bare React Native**

babel.config.js

```js
module.exports = {
  presets: ['module:@react-native/babel-preset'],
  plugins: ['@babel/plugin-transform-export-namespace-from'],
}

```

**Expo**

babel.config.js

```js
module.exports = function (api) {
  api.cache(true)
  return {
    presets: ['babel-preset-expo'],
    plugins: ['@babel/plugin-transform-export-namespace-from'],
  }
}

```

## Common errors

### `TypeError: undefined is not a function` in `randomBytes` during sign-in

The global `crypto.getRandomValues` is missing. `@web3auth/react-native-sdk/setup` installs `react-native-quick-crypto`, but it skips the install without an error if the package is missing or its native library fails to load. Check that:

- `react-native-quick-crypto` is installed in a version that matches your app (1.x for React Native 0.75 or later with the New Architecture, 0.7.x otherwise), and you rebuilt the native app after installing it.
- On Android devices with 16 KB memory pages, you use `react-native-quick-crypto` 1.x. On a 16 KB emulator, 0.7.x failed to load with `dlopen failed: empty/missing DT_HASH/DT_GNU_HASH`.
- `import '@web3auth/react-native-sdk/setup'` is the first import in your entry file.

### `Could not find method jcenter()` on Android

`@toruslabs/react-native-web-browser` 1.1.0 calls `jcenter()`, which Gradle 9 removed. Replace `jcenter()` with `mavenCentral()` in `node_modules/@toruslabs/react-native-web-browser/android/build.gradle` and persist the change with [patch-package](https://www.npmjs.com/package/patch-package).

### `minSdkVersion 23 cannot be smaller than version 24`

Your React Native version requires a higher `minSdkVersion` than your app sets (for example, React Native 0.84 requires 24). Update it in `android/build.gradle`.

### `Cannot read property 'ethereumProvider' of null`

You read `useWeb3Auth().connection` before the user signed in. `connection` is `null` until sign-in completes. Read it in event handlers or after checking `isConnected`.

### Solana: `"secretKey" expected Uint8Array of length 32, got length=64`

Add the `@web3auth/auth` Metro shim described in [Solana hooks](/embedded-wallets/sdk/react-native/solana-hooks/#3-handle-64-byte-ed25519-keys).

---

## v8 (legacy manual setup)

If you still use `@web3auth/react-native-sdk@^8.x`, follow the manual polyfill steps below. Upgrade to v10 with the [migration guide](/embedded-wallets/migration-guides/react-native/) when you can.

### 1. Install polyfill packages

**npm**

```bash
npm install --save empty-module readable-stream crypto-browserify react-native-get-random-values buffer process

```

**Yarn**

```bash
yarn add empty-module readable-stream crypto-browserify react-native-get-random-values buffer process

```

**pnpm**

```bash
pnpm add empty-module readable-stream crypto-browserify react-native-get-random-values buffer process

```

**Bun**

```bash
bun add empty-module readable-stream crypto-browserify react-native-get-random-values buffer process

```

### 2. `metro.config.js` (v8)

**Bare React Native**

metro.config.js

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

const config = {
  resolver: {
    extraNodeModules: {
      assert: require.resolve('empty-module'),
      http: require.resolve('empty-module'),
      https: require.resolve('empty-module'),
      os: require.resolve('empty-module'),
      url: require.resolve('empty-module'),
      zlib: require.resolve('empty-module'),
      path: require.resolve('empty-module'),
      crypto: require.resolve('crypto-browserify'),
      stream: require.resolve('readable-stream'),
    },
    sourceExts: [...defaultConfig.resolver.sourceExts, 'svg'],
  },
}

module.exports = mergeConfig(defaultConfig, config)

```

**Expo**

metro.config.js

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

config.resolver.extraNodeModules = {
  assert: require.resolve('empty-module'),
  http: require.resolve('empty-module'),
  https: require.resolve('empty-module'),
  os: require.resolve('empty-module'),
  url: require.resolve('empty-module'),
  zlib: require.resolve('empty-module'),
  path: require.resolve('empty-module'),
  crypto: require.resolve('crypto-browserify'),
  stream: require.resolve('readable-stream'),
  buffer: require.resolve('buffer'),
}

module.exports = config

```

### 3. `globals.js` and entry point (v8)

Create `globals.js` at your project root:

globals.js

```js
global.Buffer = require('buffer').Buffer
global.location = { protocol: 'file:' }
global.process.version = 'v16.0.0'
if (!global.process.version) {
  global.process = require('process')
}
process.browser = true

```

Then import it in your entry point:

index.js

```js
import { AppRegistry } from 'react-native'
import './globals'
import 'react-native-get-random-values'
import App from './App'
import { name as appName } from './app.json'
AppRegistry.registerComponent(appName, () => App)

```
