How to import files from outside of root directory with React Native Metro bundler

Dushyant Bansal
2 min readJan 25, 2020

--

There will be many use cases of where you’ll want to import any file outside of the root directory e.g. you may want to share code with your web project.

A simple solution could be to create a symlink inside the root directory but metro bundler doesn’t support symlink. So we’re going to see how to directly reference any file outside of root directory..

Consider this example:

common
- components
- Utils.ts, ....code
web
- node_modules
- ....code
app
- node_modules
- src/
- ...code, metro.config.js, rn-cli.config.js, etc...

Let’s say you want to access common/ from within app/ :

//Project/app/src/App.jsimport { commonFunction } from '../../common/Utils';

But this doesn’t work. When you run npm run start, metro bundler will raise the following error:

error: bundling failed: Error: Unable to resolve module `../../common/Utils` from `src/App.js`:

Solution

We can take advantage of extraNodeModules to include this parent directory and update Metro configuration.

Modified app/metro.config.js :

/**
* Metro configuration for React Native
* https://github.com/facebook/react-native
*
* @format
*/
const path = require('path');
const extraNodeModules = {
'common': path.resolve(__dirname + '/../common'),
};
const watchFolders = [
path.resolve(__dirname + '/../common')
];
module.exports = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
resolver: {
extraNodeModules
},
watchFolders,
};

Here is the link to the extraNodeModules documentation: https://facebook.github.io/metro/docs/en/configuration#resolver-options.

Since we have mapped common key to common/ path, we can include any files inside common/ relative to this path:

//Project/app/src/App.jsimport {commonFunction} from 'common/Utils';

Metro bundler will happily resolve this for us.

Now what if we want to use a dependency in commmon/ which has been installed in app/node_modules/ .

e.g. lets try to use lodash incommon/Utils.ts :

import _ from 'lodash';

where lodash is installed in app/node_modules/ .

import _ from 'lodash';common
- components
- Utils.ts, ....code
app
- node_modules
--- lodash
- src/

Run the metro bundler again and it will throw this error:

error: bundling failed: Error: Unable to resolve module `@babel/runtime/helpers/interopRequireDefault` from `../common/Utils.ts`: @babel/runtime/helpers/interopRequireDefault could not be found within the project.

Basically, metro bundler is not able to resolve dependencies used inside common/ because it’s not looking for those dependencies in app/node_modules .

Lets modify app/metro.config.js again:

/**
* Metro configuration for React Native
* https://github.com/facebook/react-native
*
* @format
*/
const path = require('path');
const extraNodeModules = {
'common': path.resolve(__dirname + '/../common'),
};
const watchFolders = [
path.resolve(__dirname + '/../common')
];
module.exports = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
resolver: {
extraNodeModules: new Proxy(extraNodeModules, {
get: (target, name) =>
//redirects dependencies referenced from common/ to local node_modules
name in target ? target[name] : path.join(process.cwd(), `node_modules/${name}`),
}),
},
watchFolders,
};

You can read more about Proxy.

Here we have configured metro bundler resolver to look for any unresolved references in app/node_modules . You can now use dependencies installed in app/node_modules from common/ .

More discussions on this topic:

--

--

Dushyant Bansal
Dushyant Bansal

Written by Dushyant Bansal

EM @Thoughtspot. Previously @ Facebook, GoGoVan. Read more about me: https://www.linkedin.com/in/dushyant37/

Responses (7)