Edit this page
Learn about different Metro bundler configurations that can be customized.
Expo CLI uses Metro during npx expo start
and npx expo export
to bundle your JavaScript code and assets. Metro is built and optimized for React Native and used for large-scale applications such as Facebook and Instagram.
You can customize the Metro bundler by creating a metro.config.js file at the root of your project. This file should export a Metro configuration that extends expo/metro-config
. Import expo/metro-config
instead of @expo/metro-config
to ensure version consistency.
Run the following command to generate the template file:
-
npx expo customize metro.config.js
The metro.config.js file looks as below:
const { getDefaultConfig } = require('expo/metro-config');
const config = getDefaultConfig(__dirname);
module.exports = config;
See metro.config.js documentation for more information.
Metro resolves files as either source code or assets. Source code is JavaScript, TypeScript, JSON, and other files used by your application. Assets are images, fonts, and other files that should not be transformed by Metro. To accommodate large-scale codebases, Metro requires all extensions for both source code and assets to be explicitly defined before starting the bundler. This is done by adding the resolver.sourceExts
and resolver.assetExts
options to the Metro configuration. By default, the following extensions are included:
assetExts
The most common customization is to include extra asset extensions to Metro.
In the metro.config.js file, add the file extension (without a leading .
) to resolver.assetExts
array:
const { getDefaultConfig } = require('expo/metro-config');
const config = getDefaultConfig(__dirname);
config.resolver.assetExts.push(
// Adds support for `.db` files for SQLite databases
'db'
);
module.exports = config;
Sometimes you want an import to be redirected to another module or file. This is called an alias. Due to the way Metro bundles for multiple platforms simultaneously, we recommend using a custom resolver to handle aliases.
In the following example, we'll add an alias for old-module
to new-module
:
const { getDefaultConfig } = require('expo/metro-config');
/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);
const ALIASES = {
'old-module': 'new-module',
};
config.resolver.resolveRequest = (context, moduleName, platform) => {
// Ensure you call the default resolver.
return context.resolveRequest(
context,
// Use an alias if one exists.
ALIASES[moduleName] ?? moduleName,
platform
);
};
module.exports = config;
If you want to only apply the alias on a certain platform, you can check the platform
argument:
config.resolver.resolveRequest = (context, moduleName, platform) => {
if (platform === 'web') {
// The alias will only be used when bundling for the web.
return context.resolveRequest(context, ALIASES[moduleName] ?? moduleName, platform);
}
// Ensure you call the default resolver.
return context.resolveRequest(context, moduleName, platform);
};
You will see the changes the next time you restart the dev server. Resolutions are never cached and do not need the --clear
flag to update. If you use a transform-based system like babel-plugin-module-resolver
, you will need to clear the cache to see changes applied.
Learn more about advanced Metro resolving in your project.
From SDK 50, Expo CLI automatically splits bundles based on async imports (web-only).
This technique can be used with Expo Router to automatically split the bundle based on route files in the app directory. It will only load the code required for the current route, and defer loading additional JavaScript until the user navigates to different pages. See Async Routes for more information.
Learn about how Expo CLI optimizes production JavaScript bundles.
Learn about customizing the JavaScript minification process in Expo CLI with Metro bundler.
Expo CLI has support for bundling websites using Metro. This is the same bundler used for native apps, and it is designed to be universal across platforms. It is the recommended bundler for web projects in Expo SDK 50 and greater.
If you previously wrote your website using the deprecated @expo/webpack-adapter
, see the migration guide and comparison chart.
Modify your app config to enable the feature using the expo.web.bundler
field:
{
"expo": {
"web": {
"bundler": "metro"
}
}
}
To start the development server run the following command:
-
npx expo start --web
Alternatively, press W in the Expo CLI terminal UI.
Expo's Metro implementation supports hosting static files from the dev server by putting them in the root public/ directory. It is similar to many other web frameworks.
When exporting with npx expo export
, the contents of the public directory are copied into the dist/ directory. It means your app can expect to fetch these assets relative to the host URL. The most common example of this is the public/favicon.ico which is used by websites to render the tab icon.
You can overwrite the default index.html in Metro web by creating a public/index.html file in your project.
In the future, this will work universally across platforms with EAS Update hosting. Currently, the feature is web-only based on the static host used for the native app, for example, the legacy Expo service updates do not support this feature.
Expo's Metro config supports the compilerOptions.paths
and compilerOptions.baseUrl
fields in the project's tsconfig.json (or jsconfig.json) file. This enables absolute imports and aliases in the project. See TypeScript guide for more information.
This feature requires additional setup in bare projects. See the Metro setup guide for more information.
Learn how to use CSS in websites that are bundled with Expo CLI and Metro bundler.