Documentation Index Fetch the complete documentation index at: https://mintlify.com/facebook/react-native/llms.txt
Use this file to discover all available pages before exploring further.
Metro is the JavaScript bundler for React Native. Learn how to configure Metro for optimal development and production builds.
Overview
Metro configuration is defined in metro.config.js at your project root. React Native provides sensible defaults through the @react-native/metro-config package.
Basic Configuration
Default Setup
Create metro.config.js:
const { getDefaultConfig , mergeConfig } = require ( '@react-native/metro-config' );
const config = {};
module . exports = mergeConfig ( getDefaultConfig ( __dirname ), config );
This uses React Native’s default Metro configuration.
Configuration Options
Metro configuration is divided into several sections:
Resolver
Controls how Metro resolves modules.
const config = {
resolver: {
// File extensions to resolve
sourceExts: [ 'jsx' , 'js' , 'ts' , 'tsx' , 'json' ],
// Asset file extensions
assetExts: [ 'png' , 'jpg' , 'jpeg' , 'gif' , 'webp' , 'svg' ],
// Platforms to support
platforms: [ 'ios' , 'android' ],
// Node modules to exclude
blockList: [ /node_modules \/ . * \/ node_modules \/ react-native \/ . * / ],
// Extra node modules to include
extraNodeModules: {},
// Unstable condition names
unstable_conditionNames: [ 'react-native' , 'browser' , 'require' ],
// Resolver main fields (package.json)
resolverMainFields: [ 'react-native' , 'browser' , 'main' ],
},
};
Controls how files are transformed.
const config = {
transformer: {
// Babel transformer path
babelTransformerPath: require . resolve ( '@react-native/metro-babel-transformer' ),
// Asset registry path
assetRegistryPath: 'react-native/Libraries/Image/AssetRegistry' ,
// Async require module path
asyncRequireModulePath: require . resolve ( 'metro-runtime/src/modules/asyncRequire' ),
// Allow optional dependencies
allowOptionalDependencies: true ,
// Transform options
getTransformOptions : async () => ({
transform: {
experimentalImportSupport: false ,
inlineRequires: true ,
},
}),
// Minifier configuration
minifierConfig: {
compress: {
drop_console: true ,
},
},
},
};
Serializer
Controls how modules are serialized into bundles.
const config = {
serializer: {
// Get polyfills
getPolyfills : () => require ( '@react-native/js-polyfills' )(),
// Modules to run before main module
getModulesRunBeforeMainModule : () => [
require . resolve ( 'react-native/Libraries/Core/InitializeCore' ),
],
// Determine if module is third-party
isThirdPartyModule : ( module ) => {
return /node_modules/ . test ( module . path );
},
// Process modules before finalizing
processModuleFilter : ( module ) => {
return true ;
},
},
};
Server
Controls Metro development server.
const config = {
server: {
// Server port
port: Number ( process . env . RCT_METRO_PORT ) || 8081 ,
// Enable HTTPS
https: false ,
// Custom SSL key/cert
// key: fs.readFileSync('path/to/key.pem'),
// cert: fs.readFileSync('path/to/cert.pem'),
},
};
Watcher
Controls file watching behavior.
const config = {
watchFolders: [
// Additional folders to watch
path . resolve ( __dirname , '../shared' ),
],
watcher: {
// Watchman configuration
watchman: {
deferStates: [ 'hg.update' ],
},
// Additional watch options
additionalExts: [ 'scss' , 'sass' ],
},
};
Common Configurations
Add Custom File Extensions
const config = {
resolver: {
sourceExts: [ 'jsx' , 'js' , 'ts' , 'tsx' , 'json' , 'mjs' , 'cjs' ],
},
};
Support SVG
Using react-native-svg-transformer:
npm install react-native-svg-transformer
const { getDefaultConfig } = require ( '@react-native/metro-config' );
const config = {
transformer: {
babelTransformerPath: require . resolve ( 'react-native-svg-transformer' ),
},
resolver: {
assetExts: getDefaultConfig ( __dirname ). resolver . assetExts . filter (
ext => ext !== 'svg'
),
sourceExts: [ ... getDefaultConfig ( __dirname ). resolver . sourceExts , 'svg' ],
},
};
module . exports = mergeConfig ( getDefaultConfig ( __dirname ), config );
Monorepo Setup
const path = require ( 'path' );
const { getDefaultConfig , mergeConfig } = require ( '@react-native/metro-config' );
const projectRoot = __dirname ;
const workspaceRoot = path . resolve ( projectRoot , '../..' );
const config = {
watchFolders: [ workspaceRoot ],
resolver: {
nodeModulesPaths: [
path . resolve ( projectRoot , 'node_modules' ),
path . resolve ( workspaceRoot , 'node_modules' ),
],
},
};
module . exports = mergeConfig ( getDefaultConfig ( projectRoot ), config );
Block Specific Modules
const config = {
resolver: {
blockList: [
// Block duplicate react-native instances
/node_modules \/ . * \/ node_modules \/ react-native \/ . * / ,
// Block specific packages
/ . * \/ __fixtures__ \/ . * / ,
/ . * \/ __tests__ \/ . * / ,
],
},
};
Custom Asset Handling
const config = {
resolver: {
assetExts: [ 'png' , 'jpg' , 'jpeg' , 'gif' , 'webp' , 'svg' , 'ttf' , 'otf' , 'woff' ],
},
transformer: {
assetPlugins: [ 'custom-asset-plugin' ],
},
};
Environment-Specific Config
const { getDefaultConfig , mergeConfig } = require ( '@react-native/metro-config' );
const isDev = process . env . NODE_ENV !== 'production' ;
const config = {
transformer: {
minifierConfig: {
compress: {
drop_console: ! isDev ,
drop_debugger: ! isDev ,
},
},
getTransformOptions : async () => ({
transform: {
experimentalImportSupport: false ,
inlineRequires: ! isDev ,
},
}),
},
};
module . exports = mergeConfig ( getDefaultConfig ( __dirname ), config );
Enable Inline Requires
const config = {
transformer: {
getTransformOptions : async () => ({
transform: {
inlineRequires: true ,
},
}),
},
};
This defers module loading until actually needed, reducing initial bundle size.
Cache Configuration
const config = {
cacheStores: [
new ( require ( 'metro-cache' ))({ root: '/tmp/metro-cache' }),
],
cacheVersion: '1.0' ,
};
Limit Worker Threads
const config = {
maxWorkers: 2 ,
};
Useful for limiting CPU usage on lower-end machines.
Tree Shaking
const config = {
transformer: {
getTransformOptions : async () => ({
transform: {
experimentalImportSupport: true ,
inlineRequires: true ,
},
}),
},
};
Production Optimizations
Remove Console Logs
const config = {
transformer: {
minifierConfig: {
compress: {
drop_console: true ,
drop_debugger: true ,
},
},
},
};
Aggressive Minification
const config = {
transformer: {
minifierPath: 'metro-minify-terser' ,
minifierConfig: {
compress: {
drop_console: true ,
reduce_funcs: true ,
collapse_vars: true ,
},
mangle: {
toplevel: true ,
},
output: {
comments: false ,
ascii_only: true ,
},
},
},
};
Advanced Use Cases
const upstreamTransformer = require ( '@react-native/metro-babel-transformer' );
module . exports . transform = async ({ src , filename , options }) => {
// Custom transformation logic
if ( filename . endsWith ( '.custom' )) {
// Transform custom files
return {
ast: null ,
code: transformedCode ,
};
}
// Use default transformer for other files
return upstreamTransformer . transform ({ src , filename , options });
};
const config = {
transformer: {
babelTransformerPath: require . resolve ( './custom-transformer' ),
},
};
const config = {
resolver: {
sourceExts: [ 'js' , 'jsx' , 'ts' , 'tsx' , 'json' ],
// Metro will look for Button.ios.js, Button.android.js, Button.native.js, Button.js
},
};
File resolution order:
Component.ios.js (on iOS)
Component.android.js (on Android)
Component.native.js
Component.js
Custom Resolver
const config = {
resolver: {
resolveRequest : ( context , moduleName , platform ) => {
// Custom resolution logic
if ( moduleName . startsWith ( 'custom:' )) {
return {
filePath: '/path/to/custom/module.js' ,
type: 'sourceFile' ,
};
}
// Use default resolution
return context . resolveRequest ( context , moduleName , platform );
},
},
};
Debugging Metro Config
Verbose Logging
REACT_NATIVE_CLI_LOGLEVEL = verbose npx react-native start
Inspect Config
Create inspect-config.js:
const config = require ( './metro.config.js' );
console . log ( JSON . stringify ( config , null , 2 ));
Run:
Test Config Changes
# Clear cache when testing config changes
npx react-native start --reset-cache
Storybook
const config = {
transformer: {
getTransformOptions : async () => ({
transform: {
experimentalImportSupport: false ,
inlineRequires: true ,
},
}),
},
resolver: {
resolverMainFields: [ 'sbmodern' , 'react-native' , 'browser' , 'main' ],
},
};
Web Support (React Native Web)
const config = {
resolver: {
platforms: [ 'ios' , 'android' , 'web' ],
resolverMainFields: [ 'react-native' , 'browser' , 'main' ],
},
};
Troubleshooting
Module Not Found
Solution:
Check resolver configuration:
const config = {
resolver: {
extraNodeModules: {
'module-name' : path . resolve ( __dirname , 'path/to/module' ),
},
},
};
Cache Issues
Solution:
npx react-native start --reset-cache
rm -rf /tmp/metro- *
Slow Bundling
Solution:
Limit workers:
const config = {
maxWorkers: 2 ,
};
Use persistent cache
Exclude unnecessary files from watching
Duplicate Module
Error: Requiring module 'X', which was already loaded
Solution:
const config = {
resolver: {
blockList: [
/node_modules \/ . * \/ node_modules \/ react-native \/ . * / ,
],
},
};
React Native Defaults
The default Metro configuration from @react-native/metro-config includes:
{
resolver : {
resolverMainFields : [ 'react-native' , 'browser' , 'main' ],
platforms : [ 'android' , 'ios' ],
unstable_conditionNames : [ 'react-native' ],
},
serializer : {
getModulesRunBeforeMainModule : () => [
require . resolve ( 'react-native/Libraries/Core/InitializeCore' ),
],
getPolyfills : () => require ( '@react-native/js-polyfills' )(),
},
server : {
port : Number ( process . env . RCT_METRO_PORT ) || 8081 ,
},
transformer : {
allowOptionalDependencies : true ,
assetRegistryPath : 'react-native/Libraries/Image/AssetRegistry' ,
asyncRequireModulePath : require . resolve ( 'metro-runtime/src/modules/asyncRequire' ),
babelTransformerPath : require . resolve ( '@react-native/metro-babel-transformer' ),
getTransformOptions : async () => ({
transform: {
experimentalImportSupport: false ,
inlineRequires: true ,
},
}),
},
watchFolders : [],
}
Best Practices
Always Merge with Defaults
Use mergeConfig to preserve React Native defaults: const { getDefaultConfig , mergeConfig } = require ( '@react-native/metro-config' );
module . exports = mergeConfig ( getDefaultConfig ( __dirname ), config );
Clear Cache After Config Changes
npx react-native start --reset-cache
Use Environment Variables
const isDev = process . env . NODE_ENV !== 'production' ;
const config = {
transformer: {
minifierConfig: {
compress: { drop_console: ! isDev }
}
}
};
Document Custom Configurations
Add comments explaining why custom configurations are needed.
Next Steps
Start Command Start Metro development server
Bundle Command Create production bundles
Metro Docs Official Metro documentation
Performance Optimize bundle performance