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

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.

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 install @web3auth/react-native-sdk
npm install --save-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. The setup module installs it as the global crypto object. See 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:

index.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)

3. Update metro.config.js​

metro.config.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)

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.

babel.config.js
module.exports = {
presets: ['module:@react-native/babel-preset'],
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.

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.


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 when you can.

1. Install polyfill packages​

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

2. metro.config.js (v8)​

metro.config.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)

3. globals.js and entry point (v8)​

Create globals.js at your project root:

globals.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
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)