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.
v9 (recommended)
SDK v9 ships a single helper that configures Metro and polyfills automatically. No manual extraNodeModules is needed.
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
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'],
}
}
v8 (legacy manual setup)
If you are still using @web3auth/react-native-sdk@^8.x, follow the manual polyfill steps below. We recommend upgrading to v9.
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)