What is @expo/metro-config?
@expo/metro-config is a configuration package for Metro, the JavaScript bundler used by React Native. It provides a set of default configurations optimized for Expo projects, making it easier to set up and customize the Metro bundler for your React Native applications.
What are @expo/metro-config's main functionalities?
Default Configuration
This feature provides a default configuration for Metro bundler, optimized for Expo projects. It simplifies the setup process by providing sensible defaults.
const { getDefaultConfig } = require('@expo/metro-config');
const defaultConfig = getDefaultConfig(__dirname);
module.exports = defaultConfig;
Custom Configuration
This feature allows you to customize the default Metro configuration. For example, you can add custom file extensions to the resolver.
const { getDefaultConfig } = require('@expo/metro-config');
const defaultConfig = getDefaultConfig(__dirname);
defaultConfig.resolver.sourceExts.push('cjs');
module.exports = defaultConfig;
Asset Plugins
This feature allows you to add custom asset plugins to the Metro configuration. For example, you can add a plugin to hash asset files.
const { getDefaultConfig } = require('@expo/metro-config');
const defaultConfig = getDefaultConfig(__dirname);
defaultConfig.transformer.assetPlugins = ['expo-asset/tools/hashAssetFiles'];
module.exports = defaultConfig;
Other packages similar to @expo/metro-config
metro-config
metro-config is the official configuration package for Metro bundler. It provides a flexible way to customize the Metro bundler for React Native projects. Unlike @expo/metro-config, it does not come with Expo-specific defaults.
haul
haul is an alternative JavaScript bundler for React Native that offers more customization options compared to Metro. It can be used as a replacement for Metro and provides its own set of configuration options, making it more flexible but also more complex to set up.
👋 Welcome to
@expo/metro-config
A Metro config for running React Native projects with the Metro bundler.
Exotic
When enabled, exotic mode adds the following assumptions:
- Resolver Fields:
browser, main
- The
react-native
field in module package.json
is NOT supported. - Packages using
react-native-builder-bob
will default to using the CommonJS setting in exotic. If you need to modify your Node modules manually, be sure to change the files in your lib/commonjs/
folder.
- Extensions:
ts, tsx, js, jsx, json, cjs
.babelrc
support is removed in favor of babel.config.js
.x_facebook_sources
is toggled off by default.
Default Rules
- Modules with
.*/lib/commonjs/
are skipped. - React Native is transformed with Sucrase to remove flow types and other unsupported language features.
- If the React Native team transpiles react-native before shipping, we can remove this step.
- Expo modules are transformed with Sucrase to remove import/export syntax. This is temporary while we figure out how to add ESModule support to the native runtime.
- This is for improved tree shaking.
- Known community modules (especially ones included in Expo Go) are transformed using a more expensive Sucrase preset
- We may add support for extending this list in the future.
- All other node modules are skipped.
- All remaining code is assumed to be application code and transpiled with your local Babel preset.
- "Victory Native" packages use too many language features so they are transpiled with Babel.
Experimental
You can use @expo/metro-config/transformer
to extend the experimental transformer API.
This can be used for:
- Adding extra modules that need to be transpiled locally (
transpileModules
). - Adding extra
nodeModulesPaths
for monorepo support. - Adding support for the
react-native
main resolver field back.
metro-exotic-transformer.js
const { createExoticTransformer } = require('@expo/metro-config/transformer');
module.exports = createExoticTransformer({
transpileModules: ['@stripe/stripe-react-native'],
});
Then use it in your project:
metro.config.js
const { getDefaultConfig } = require('@expo/metro-config');
const config = getDefaultConfig(__dirname, {
mode: 'exotic',
});
config.transformer.babelTransformerPath = require.resolve('./metro-exotic-transformer');
module.exports = config;
Source Maps
Metro bundler adds an undocumented extension to source maps which provides slightly different names for anonymous functions. The source map sizes increase a lot by adding the x_facebook_sources
object, and the net transformation time also increases by a noticeable amount. By default, exotic disables this feature. The feature can be re-enabled with EXPO_USE_FB_SOURCES
. Here are the results:
Enabled | Disabled |
---|
iOS Bundling: 7664ms | iOS Bundling: 6875ms |
| |
- Most error reporting services don't support
x_facebook_sources
so the larger size mostly just increases hosting costs (when uploaded). - Documentation for
x_facebook_sources
is not provided.
Cite: #3861
Troubleshooting
You should see the following log when Exotic is enabled:
Unstable feature EXPO_USE_EXOTIC is enabled. Bundling may not work as expected, and is subject to breaking changes.
Or if EXPO_DEBUG=1
is enabled, you'll see exotic mode in the settings breakdown.
If you don't see this message, check to ensure your metro.config.js
is using @expo/metro-config
and the version is at least 0.2.2
.
The transformer can be debugged using the environment variable: DEBUG=expo:metro:exotic-babel-transformer
or DEBUG=expo:metro:*
Adding Resolver Fields
You can add the react-native
field back manually when exotic mode is enabled, we will investigate adding it back after more community packages have had time to adjust to transforming their code ahead of time.
metro.config.js
const { getDefaultConfig } = require('@expo/metro-config');
const config = getDefaultConfig(__dirname);
config.resolver.resolverMainFields.unshift('react-native');
module.exports = config;