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
- Yarn
- pnpm
- Bun
npm install @web3auth/react-native-sdk
npm install --save-dev @babel/plugin-transform-export-namespace-from
yarn add @web3auth/react-native-sdk
yarn add --dev @babel/plugin-transform-export-namespace-from
pnpm add @web3auth/react-native-sdk
pnpm add --save-dev @babel/plugin-transform-export-namespace-from
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. 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:
- Bare React Native
- Expo
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)
Create index.ts and set "main": "index.ts" in package.json:
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
- Expo
const { getDefaultConfig } = require('@react-native/metro-config')
const { withWeb3Auth } = require('@web3auth/react-native-sdk/metro-config')
const config = getDefaultConfig(__dirname)
module.exports = withWeb3Auth(config)
const { getDefaultConfig } = require('expo/metro-config')
const { withWeb3Auth } = require('@web3auth/react-native-sdk/metro-config')
const config = getDefaultConfig(__dirname)
module.exports = withWeb3Auth(config)
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
- Expo
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: ['@babel/plugin-transform-export-namespace-from'],
}
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-cryptois 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-crypto1.x. On a 16 KB emulator, 0.7.x failed to load withdlopen 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
- Yarn
- pnpm
- Bun
npm install --save empty-module readable-stream crypto-browserify react-native-get-random-values buffer process
yarn add empty-module readable-stream crypto-browserify react-native-get-random-values buffer process
pnpm add empty-module readable-stream crypto-browserify react-native-get-random-values buffer process
bun add empty-module readable-stream crypto-browserify react-native-get-random-values buffer process
2. metro.config.js (v8)
- Bare React Native
- Expo
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)
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:
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:
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)