Skip to main content

Configuring Metro

A Metro config can be created in these three ways (ordered by priority):

  1. metro.config.js
  2. metro.config.json
  3. The metro field in package.json

You can also give a custom file to the configuration by specifying --config <path/to/config> when calling the CLI.


When Metro is started via the React Native CLI, some defaults are different from those mentioned below. See the React Native repository for details.

Configuration Structure​

The configuration is based on our concepts, which means that for every module we have a separate config option. A common configuration structure in Metro looks like this:

module.exports = {
/* general options */

resolver: {
/* resolver options */
transformer: {
/* transformer options */
serializer: {
/* serializer options */
server: {
/* server options */
watcher: {
/* watcher options */
watchman: {
/* Watchman-specific options */

General Options​


Type: CacheStores (see details below)

A list of storage adapters for Metro's transformer cache. This can be any combination of built-in cache stores and custom cache stores. Defaults to using a temporary directory on disk as the only cache store.

When Metro needs to transform a module, it first computes a machine-independent cache key for that file, and uses it to try to read from each of the stores in order. Once Metro has obtained the output of the transformer (whether already cached or not), it writes the transform result to all of the stores that returned null (a cache miss) for that key.

type CacheStores =
| Array<CacheStore<Buffer | JsonSerializable>>
| ((MetroCache) => Array<
CacheStore<Buffer | JsonSerializable>

// The exports of 'metro-cache'
type MetroCache = {

type JsonSerializable = /* Any JSON-serializable value */;


Type: string

An arbitrary string appended to all cache keys in the project before they are hashed. There is generally no need to set this explicitly, as Metro will automatically derive the correct cache keys from your project config and the contents of source files.


Type: string

The root folder of your project. If your project depends on any files outside this root, their containing directories must be listed in watchFolders.


If your Metro project is developed in a monorepo and includes files from multiple logical packages, you'll generally want to set projectRoot to the root of your repository, or at least high enough in the hierarchy that all relevant files are reachable without separately configuring watchFolders.


Type: Array<string>

A list of directories outside of projectRoot that can contain source files for the project.


Despite the naming of this option, it isn't related solely to file watching. Even in an offline build (for example, in CI), all files must be visible to Metro through the combination of watchFolders and projectRoot.


Type: string

The absolute path of a module (or a package name resolvable from the metro package) that implements a transformer.

See the implementation of Metro's default transformer (metro-transform-worker) for more information about the transformer interface.


Type: {update: (event: ReportableEvent) => void}

Used to report the status of the bundler during the bundling process. The default implementation prints most events to the terminal.

See also the definition of ReportableEvent in Metro's source code.


Type: boolean

If true, Metro will reset the transformer cache (see cacheStores) and the file map cache (see fileMapCacheDirectory) on startup.


Type: boolean

If true, Metro will use a stable mapping from files to transformer workers, so the same file is always transformed by the same worker. This can improve initial build performance if the transformer is expensive to initialize, but can slow down concurrent builds with different configurations (e.g. multiple React Native apps connected to one Metro server). Defaults to true.


Type: number

The number of workers to use for parallel processing in Metro. Defaults to approximately half of the number of cores available on the machine, as reported by os.cpus().

  1. Values exceeding the number of available cores have no effect.
  2. If maxWorkers is set to 1 or lower, worker code will run in the main Metro process instead of concurrently.
  3. Metro has two separate worker pools - one for transformation and one for building the file map. Each pool has its worker count set to maxWorkers independently.


Type: string

The path to the metro-file-map cache directory, defaults to os.tmpdir().


Type: string

Alias of fileMapCacheDirectory

Resolver Options​


Type: Array<string>

The list of asset file extensions to include in the bundle. For example, including 'ttf' allows Metro bundles to reference .ttf files. This is used primarily to enable React Native's image asset support. The default list includes many common image, video and audio file extensions. See Metro's source code for the full list.


Type: Array<string>

The list of source file extensions to include in the bundle. For example, including 'ts' allows Metro to include .ts files in the bundle.

The order of these extensions defines the order to match files on disk. For more information, see Module Resolution.

Defaults to ['js', 'jsx', 'json', 'ts', 'tsx'].


Type: Array<string>

The list of fields in package.json that Metro will treat as describing a package's entry points. The default is ['browser', 'main'], so the resolver will use the browser field if it exists and main otherwise.

Metro's default resolver processes each of these fields according to the browser field spec, including the ability to replace and ignore specific files. For more information, see Module Resolution.


When Metro is started via the React Native CLI, resolverMainFields defaults to ['react-native', 'browser', 'main'].


Type: boolean

Whether to disable looking up modules in node_modules folders. This only affects the default search through the directory tree, not other Metro options like extraNodeModules or nodeModulesPaths. Defaults to false.


Type: string

What module to use as the canonical "empty" module when one is needed. Defaults to using the one included in metro-runtime. You only need to change this if Metro is installed outside of your project.


Type: {[string]: string}

A mapping of package names to directories that is consulted after the standard lookup through node_modules as well as any nodeModulesPaths. For more information, see Module Resolution.


Type: Array<string>

A list of paths to check for modules after looking through all node_modules directories. This is useful if third-party dependencies are installed in a different location outside of the direct path of source files. For more information, see Module Resolution.


Type: ?CustomResolver

An optional function used to override the default resolution algorithm. This is particularly useful for cases where aliases or custom protocols are used. For example:

resolveRequest: (context, moduleName, platform) => {
if (moduleName.startsWith('my-custom-resolver:')) {
// Logic to resolve the module name to a file path...
// NOTE: Throw an error if there is no resolution.
return {
filePath: 'path/to/file',
type: 'sourceFile',
// Optionally, chain to the standard Metro resolver.
return context.resolveRequest(context, moduleName, platform);

For more information on customizing the resolver, see Module Resolution.


Type: boolean

If set to false, prevents Metro from using Watchman (even if it's installed).


Type: RegExp or Array<RegExp>

A regular expression (or list of regular expressions) defining which paths to exclude from Metro's file map. Files whose absolute paths match these patterns are effectively hidden from Metro and cannot be resolved or imported in the current project.


Type: ?string

The path to the Haste implementation for the current project. Haste is an opt-in mechanism for importing modules by their globally-unique name anywhere in the project, e.g. import Foo from 'Foo'.

Metro expects this module to have the following signature:

module.exports = {
getHasteName(filePath: string): ?string {
// ...

getHasteName should return a short, globally unique name for the module whose path is filePath, or null if the module should not be accessible via Haste.


Type: Array<string>

Additional platforms to resolve. Defaults to ['ios', 'android', 'windows', 'web'].

For more information, see Module Resolution and React Native's documentation for platform-specific extensions.


Type: Array<RegExp>

In development mode, suppress require cycle warnings for any cycle involving a module that matches any of these expressions. This is useful for third-party code and first-party expected cycles.

Note that if you specify your own value for this config option it will replace (not concatenate with) Metro's default.

Defaults to [/(^|\/|\\)node_modules($|\/|\\)/].

Transformer Options​


Type: string

The name of a module that provides the asyncRequire function, which is used to implement dynamic import() at runtime. Defaults to metro-runtime/src/modules/asyncRequire.


The module named by asyncRequireModulePath is resolved relative to the module containing the original import() call. In particular, assuming the default value of asyncRequireModulePath is in use, the project must have a compatible version of metro-runtime installed in node_modules.


Although Metro doesn't perform bundle splitting out of the box, a custom asyncRequire implementation can be used as part of a bundle splitting solution:

// Get a reference to the dynamic `require` function provided by Metro.
const dynamicRequire = (require: {importAll: mixed => mixed});

module.exports = async function asyncRequire(moduleID: mixed): Promise<mixed> {
// 1. Do any work necessary (not detailed here) to fetch and evaluate the
// module's code, as transformed by Metro.
// 2. Require the module from Metro's module registry using `dynamicRequire`.
return dynamicRequire.importAll(moduleID);


Type: 'throwAtRuntime' | 'reject'

Controls how Metro handles dependencies that cannot be statically analyzed at build time. For example, require('./' + someFunction() + '.js') cannot be resolved without knowing what someFunction() will return.

  • 'throwAtRuntime' (the default): Metro does not stop bundling, but the require call will throw at runtime.
  • 'reject': Metro will stop bundling and report an error to the user.


Type: Function (see details below)

A function called by Metro to calculate additional options for the transformer and serializer based on the specific bundle being built.

Metro expects getTransformOptions to have the following signature:

function getTransformOptions(
entryPoints: $ReadOnlyArray<string>,
options: {
dev: boolean,
hot: boolean,
platform: ?string,
getDependenciesOf: (path: string) => Promise<Array<string>>,
): Promise<ExtraTransformOptions> {
// ...

getTransformOptions receives these parameters:

  • entryPoints: Absolute paths to the bundle's entry points (typically just one).
  • options:
    • dev: Whether the bundle is being built in development mode.
    • hot:
      Always true.
    • platform: The target platform (e.g. ios, android).
  • getDependenciesOf: A function which, given an absolute path to a module, returns a promise that resolves to the absolute paths of the module's transitive dependencies.

getTransformOptions should return a promise that resolves to an object with the following properties:

type ExtraTransformOptions = {
preloadedModules?: {[path: string]: true} | false,
ramGroups?: Array<string>,
transform?: {
inlineRequires?: {blockList: {[string]: true}} | boolean,
nonInlinedRequires?: $ReadOnlyArray<string>,
  • preloadedModules: A plain object whose keys represent a set of absolute paths. When serializing an indexed RAM bundle, the modules in this set will be marked for eager evaluation at runtime.
  • ramGroups: An array of absolute paths. When serializing an indexed RAM bundle, each of the listed modules will be serialized along with its transitive dependencies. At runtime, the modules will all be parsed together as soon as any one of them is evaluated.
  • transform: Advanced options for the transformer.
    • inlineRequires:
      • If inlineRequires is a boolean, it controls whether inline requires are enabled in this bundle.
      • If inlineRequires is an object, inline requires are enabled in all modules, except ones whose absolute paths appear as keys of inlineRequires.blockList.
    • nonInlinedRequires: An array of unresolved module specifiers (e.g. react, react-native) to never inline, even when inline requires are enabled.


Type: string (default: 'metro-minify-terser')

Path, or package name resolvable from metro-transform-worker, to the minifier that minifies the code after transformation.


Type: {[key: string]: mixed}

Configuration object that will be passed to the minifier (it should be serializable).


Type: number

Define a threshold (in bytes) to disable some expensive optimizations for big files.

React Native Only​


Type: Array<string>

List of modules to call to modify Asset data


Type: string

Where to fetch the assets from.

Babel-specific transformer options​


Type: string

The name of a module that compiles code with Babel, returning an AST and optional metadata. Defaults to metro-babel-transformer.

Refer to the source code of metro-babel-transformer and metro-react-native-babel-transformer for details on implementing a custom Babel transformer.


This option only has an effect under the default transformerPath. Custom transformers may ignore it.


Type: boolean

Whether to enable searching for Babel configuration files. This is passed to Babel as the babelrc config option. Defaults to true.


This option only has an effect under the default transformerPath. Custom transformers may ignore it. Custom Babel transformers should respect this option.


Type: boolean | string

Whether the transformer should use the @babel/transform/runtime plugin. Defaults to true.

If the value is a string, it is treated as a runtime version number and passed as version to the @babel/plugin-transform-runtime configuration. This allows you to optimize the generated Babel runtime calls based on the version installed in your project.


This option only works under the default settings for React Native. It may have no effect in a project that uses custom transformerPath, a custom babelTransformerPath or a custom Babel config file.


Type: boolean

Whether to use the hermes-parser package to parse JavaScript source files, instead of Babel. Defaults to false.


This option only has an effect under the default transformerPath and the Babel transformers built into Metro. Custom transformers and custom Babel transformers may ignore it.

Serializer Options​


Type: (number | string) => string

Specify the format of the initial require statements that are appended at the end of the bundle. By default is __r(${moduleId});.


Type: () => (path: string) => number

Used to generate the module id for require statements.


Type: ({platform: ?string}) => $ReadOnlyArray<string>

An optional list of polyfills to include in the bundle. The list defaults to a set of common polyfills for Number, String, Array, Object...


Type: (entryFilePath: string) => Array<string>

An array of modules to be required before the entry point. It should contain the absolute path of each module. Note that this will add the additional require statements only if the passed modules are already included as part of the bundle.


Type: (module: Array<Module>) => boolean

A filter function to discard specific modules from the output.

Server Options​

These options are used when Metro serves the content.


Type: number

Which port to listen on.


Type: boolean

Whether we should enable CMD+R hotkey for refreshing the bundle.


Type: (Middleware, Server) => Middleware

The possibility to add custom middleware to the server response chain.


Type: string => string

A function that will be called every time Metro processes a URL. Metro will use the return value of this function as if it were the original URL provided by the client. This applies to all incoming HTTP requests (after any custom middleware), as well as bundle URLs in /symbolicate request payloads and within the hot reloading protocol.


Type: boolean (default: true)

Run Inspector Proxy server inside Metro to be able to inspect React Native code.

Watcher Options​

Options for the filesystem watcher.


Dot notation in this section indicates a nested configuration object, e.g. watchman.deferStates β†’ watchman: { deferStates: ... }.


Type: Array<string>

The extensions which Metro should watch in addition to sourceExts, but which will not be automatically tried by the resolver.

Therefore, the two behavior differences from resolver.sourceExts when importing a module are:

  • Modules can only be required when fully specified (e.g. import moduleA from 'moduleA.mjs').
  • No platform-specific resolution is performed.

Defaults to ['cjs', 'mjs'].


Type: boolean

Whether to periodically check the health of the filesystem watcher by writing a temporary file to the project and waiting for it to be observed.

The default value is false.


Type: string

If watcher health checks are enabled, this property controls the name of the temporary file that will be written into the project filesystem.

The default value is '.metro-health-check'.


There's no need to commit health check files to source control. If you choose to enable health checks in your project, make sure you add .metro-health-check* to your .gitignore file to avoid generating unnecessary changes.


Type: number

If watcher health checks are enabled, this property controls how often they occur (in milliseconds).

The default value is 30000.


Type: number

If watcher health checks are enabled, this property controls the time (in milliseconds) Metro will wait for a file change to be observed before considering the check to have failed.

The default value is 5000.


Type: Array<string>

Applies when using Watchman. Metro will defer processing filesystem updates while these states are asserted in the watch. This is useful for debouncing builds while the filesystem hasn't settled, e.g. during large source control operations.

The default value is ['hg.update'].

Merging Configurations​

Using the metro-config package it is possible to merge multiple configurations together.

mergeConfig(...configs): MergedConfigReturns the merged configuration of two or more configuration objects.

Arrays and function based config parameters do not deeply merge and will instead override any pre-existing config parameters. This allows overriding and removing default config parameters such as platforms or getModulesRunBeforeMainModule that may not be required in your environment.

Merging Example​

// metro.config.js
const { mergeConfig } = require('metro-config');

const configA = {
/* general options */

resolver: {
/* resolver options */
transformer: {
/* transformer options */
serializer: {
/* serializer options */
server: {
/* server options */

const configB = {
/* general options */

resolver: {
/* resolver options */
transformer: {
/* transformer options */
serializer: {
/* serializer options */
server: {
/* server options */

module.exports = mergeConfig(configA, configB);