Skip to content

Commit 36e4b99

Browse files
authored
[docs] Add Metro guide, update Metro config references (#3772)
1 parent d670a26 commit 36e4b99

File tree

6 files changed

+230
-20
lines changed

6 files changed

+230
-20
lines changed

docs/metro.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
---
2+
id: metro
3+
title: Metro
4+
---
5+
6+
React Native uses [Metro](https://facebook.github.io/metro/) to build your JavaScript code and assets.
7+
8+
## Configuring Metro
9+
10+
Configuration options for Metro can be customized in your project's `metro.config.js` file. This can export either:
11+
12+
- **An object (recommended)** that will be merged on top of Metro's internal config defaults.
13+
- [**A function**](#advanced-using-a-config-function) that will be called with Metro's internal config defaults and should return a final config object.
14+
15+
:::tip
16+
Please see [**Configuring Metro**](https://facebook.github.io/metro/docs/configuration) on the Metro website for documentation on all available config options.
17+
:::
18+
19+
In React Native, your Metro config should extend either [@react-native/metro-config](https://www.npmjs.com/package/@react-native/metro-config) or [@expo/metro-config](https://www.npmjs.com/package/@expo/metro-config). These packages contain essential defaults necessary to build and run React Native apps.
20+
21+
Below is the default `metro.config.js` file in a React Native template project:
22+
23+
<!-- prettier-ignore -->
24+
```js
25+
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
26+
27+
/**
28+
* Metro configuration
29+
* https://facebook.github.io/metro/docs/configuration
30+
*
31+
* @type {import('metro-config').MetroConfig}
32+
*/
33+
const config = {};
34+
35+
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
36+
```
37+
38+
Metro options you wish to customize can be done so within the `config` object. We strongly recommend defining all config values statically within this file.
39+
40+
### Advanced: Using a config function
41+
42+
Exporting a config function is an opt-in to managing the final config yourself — **Metro will not apply any internal defaults**. This pattern can be useful when needing to read the base default config object from Metro or to set options dynamically.
43+
44+
:::info
45+
**From @react-native/metro-config `0.72.1`**, it is no longer necessary to use a config function to access the complete default config. See the **Tip** section below.
46+
:::
47+
48+
<!-- prettier-ignore -->
49+
```js
50+
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
51+
52+
module.exports = function (baseConfig) {
53+
const defaultConfig = mergeConfig(baseConfig, getDefaultConfig(__dirname));
54+
const {resolver: {assetExts, sourceExts}} = defaultConfig;
55+
56+
return mergeConfig(
57+
defaultConfig,
58+
{
59+
resolver: {
60+
assetExts: assetExts.filter(ext => ext !== 'svg'),
61+
sourceExts: [...sourceExts, 'svg'],
62+
},
63+
},
64+
);
65+
};
66+
```
67+
68+
:::tip
69+
Using a config function is for advanced use cases. A simpler method than the above, e.g. for customising `sourceExts`, would be to read these defaults from **@react-native/metro-config**.
70+
71+
**Alternative**
72+
73+
<!-- prettier-ignore -->
74+
```js
75+
const defaultConfig = getDefaultConfig(__dirname);
76+
77+
const config = {
78+
resolver: {
79+
sourceExts: [...defaultConfig.resolver.sourceExts, 'svg'],
80+
},
81+
};
82+
83+
module.exports = mergeConfig(defaultConfig, config);
84+
```
85+
86+
**However!**, we recommend copying and editing when overriding these config values — placing the source of truth in your config file.
87+
88+
**Recommended**
89+
90+
<!-- prettier-ignore -->
91+
```js
92+
const config = {
93+
resolver: {
94+
sourceExts: ['js', 'ts', 'tsx', 'svg'],
95+
},
96+
};
97+
```
98+
99+
:::
100+
101+
## Learn more about Metro
102+
103+
- [Metro website](https://facebook.github.io/metro/)
104+
- [Video: "Metro & React Native DevX" talk at App.js 2023](https://www.youtube.com/watch?v=c9D4pg0y9cI)

docs/ram-bundles-inline-requires.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -148,21 +148,20 @@ Every app is different, but it may make sense to only load the modules you need
148148

149149
We now need to update `metro.config.js` in the root of the project to use our newly generated `modulePaths.js` file:
150150

151+
<!-- prettier-ignore -->
151152
```js
152-
const modulePaths = require('./packager/modulePaths');
153-
const resolve = require('path').resolve;
153+
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
154154
const fs = require('fs');
155-
156-
// Update the following line if the root folder of your app is somewhere else.
157-
const ROOT_FOLDER = resolve(__dirname, '..');
155+
const path = require('path');
156+
const modulePaths = require('./packager/modulePaths');
158157

159158
const config = {
160159
transformer: {
161160
getTransformOptions: () => {
162161
const moduleMap = {};
163-
modulePaths.forEach(path => {
164-
if (fs.existsSync(path)) {
165-
moduleMap[resolve(path)] = true;
162+
modulePaths.forEach(modulePath => {
163+
if (fs.existsSync(modulePath)) {
164+
moduleMap[path.resolve(modulePath)] = true;
166165
}
167166
});
168167
return {
@@ -171,12 +170,13 @@ const config = {
171170
};
172171
},
173172
},
174-
projectRoot: ROOT_FOLDER,
175173
};
176174

177-
module.exports = config;
175+
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
178176
```
179177

178+
See also [**Configuring Metro**](/docs/metro#configuring-metro).
179+
180180
The `preloadedModules` entry in the config indicates which modules should be marked as preloaded when building a RAM bundle. When the bundle is loaded, those modules are immediately loaded, before any requires have even executed. The `blockList` entry indicates that those modules should not be required inline. Because they are preloaded, there is no performance benefit from using an inline require. In fact the generated JavaScript spends extra time resolving the inline require every time the imports are referenced.
181181

182182
## Test and Measure Improvements

website/sidebars.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"Workflow": [
2222
"running-on-device",
2323
"fast-refresh",
24+
"metro",
2425
{
2526
"type": "category",
2627
"label": "Debugging",
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
---
2+
id: metro
3+
title: Metro
4+
---
5+
6+
React Native uses [Metro](https://facebook.github.io/metro/) to build your JavaScript code and assets.
7+
8+
## Configuring Metro
9+
10+
Configuration options for Metro can be customized in your project's `metro.config.js` file. This can export either:
11+
12+
- **An object (recommended)** that will be merged on top of Metro's internal config defaults.
13+
- [**A function**](#advanced-using-a-config-function) that will be called with Metro's internal config defaults and should return a final config object.
14+
15+
:::tip
16+
Please see [**Configuring Metro**](https://facebook.github.io/metro/docs/configuration) on the Metro website for documentation on all available config options.
17+
:::
18+
19+
In React Native, your Metro config should extend either [@react-native/metro-config](https://www.npmjs.com/package/@react-native/metro-config) or [@expo/metro-config](https://www.npmjs.com/package/@expo/metro-config). These packages contain essential defaults necessary to build and run React Native apps.
20+
21+
Below is the default `metro.config.js` file in a React Native template project:
22+
23+
<!-- prettier-ignore -->
24+
```js
25+
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
26+
27+
/**
28+
* Metro configuration
29+
* https://facebook.github.io/metro/docs/configuration
30+
*
31+
* @type {import('metro-config').MetroConfig}
32+
*/
33+
const config = {};
34+
35+
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
36+
```
37+
38+
Metro options you wish to customize can be done so within the `config` object. We strongly recommend defining all config values statically within this file.
39+
40+
### Advanced: Using a config function
41+
42+
Exporting a config function is an opt-in to managing the final config yourself — **Metro will not apply any internal defaults**. This pattern can be useful when needing to read the base default config object from Metro or to set options dynamically.
43+
44+
:::info
45+
**From @react-native/metro-config `0.72.1`**, it is no longer necessary to use a config function to access the complete default config. See the **Tip** section below.
46+
:::
47+
48+
<!-- prettier-ignore -->
49+
```js
50+
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
51+
52+
module.exports = function (baseConfig) {
53+
const defaultConfig = mergeConfig(baseConfig, getDefaultConfig(__dirname));
54+
const {resolver: {assetExts, sourceExts}} = defaultConfig;
55+
56+
return mergeConfig(
57+
defaultConfig,
58+
{
59+
resolver: {
60+
assetExts: assetExts.filter(ext => ext !== 'svg'),
61+
sourceExts: [...sourceExts, 'svg'],
62+
},
63+
},
64+
);
65+
};
66+
```
67+
68+
:::tip
69+
Using a config function is for advanced use cases. A simpler method than the above, e.g. for customising `sourceExts`, would be to read these defaults from **@react-native/metro-config**.
70+
71+
**Alternative**
72+
73+
<!-- prettier-ignore -->
74+
```js
75+
const defaultConfig = getDefaultConfig(__dirname);
76+
77+
const config = {
78+
resolver: {
79+
sourceExts: [...defaultConfig.resolver.sourceExts, 'svg'],
80+
},
81+
};
82+
83+
module.exports = mergeConfig(defaultConfig, config);
84+
```
85+
86+
**However!**, we recommend copying and editing when overriding these config values — placing the source of truth in your config file.
87+
88+
**Recommended**
89+
90+
<!-- prettier-ignore -->
91+
```js
92+
const config = {
93+
resolver: {
94+
sourceExts: ['js', 'ts', 'tsx', 'svg'],
95+
},
96+
};
97+
```
98+
99+
:::
100+
101+
## Learn more about Metro
102+
103+
- [Metro website](https://facebook.github.io/metro/)
104+
- [Video: "Metro & React Native DevX" talk at App.js 2023](https://www.youtube.com/watch?v=c9D4pg0y9cI)

website/versioned_docs/version-0.72/ram-bundles-inline-requires.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -148,21 +148,20 @@ Every app is different, but it may make sense to only load the modules you need
148148

149149
We now need to update `metro.config.js` in the root of the project to use our newly generated `modulePaths.js` file:
150150

151+
<!-- prettier-ignore -->
151152
```js
152-
const modulePaths = require('./packager/modulePaths');
153-
const resolve = require('path').resolve;
153+
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
154154
const fs = require('fs');
155-
156-
// Update the following line if the root folder of your app is somewhere else.
157-
const ROOT_FOLDER = resolve(__dirname, '..');
155+
const path = require('path');
156+
const modulePaths = require('./packager/modulePaths');
158157

159158
const config = {
160159
transformer: {
161160
getTransformOptions: () => {
162161
const moduleMap = {};
163-
modulePaths.forEach(path => {
164-
if (fs.existsSync(path)) {
165-
moduleMap[resolve(path)] = true;
162+
modulePaths.forEach(modulePath => {
163+
if (fs.existsSync(modulePath)) {
164+
moduleMap[path.resolve(modulePath)] = true;
166165
}
167166
});
168167
return {
@@ -171,12 +170,13 @@ const config = {
171170
};
172171
},
173172
},
174-
projectRoot: ROOT_FOLDER,
175173
};
176174

177-
module.exports = config;
175+
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
178176
```
179177

178+
See also [**Configuring Metro**](/docs/metro#configuring-metro).
179+
180180
The `preloadedModules` entry in the config indicates which modules should be marked as preloaded when building a RAM bundle. When the bundle is loaded, those modules are immediately loaded, before any requires have even executed. The `blockList` entry indicates that those modules should not be required inline. Because they are preloaded, there is no performance benefit from using an inline require. In fact the generated JavaScript spends extra time resolving the inline require every time the imports are referenced.
181181

182182
## Test and Measure Improvements

website/versioned_sidebars/version-0.72-sidebars.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"Workflow": [
2222
"running-on-device",
2323
"fast-refresh",
24+
"metro",
2425
{
2526
"type": "category",
2627
"label": "Debugging",

0 commit comments

Comments
 (0)