How to Import Files from Outside of Root directory in React Native
Tags: reactnative, metrobundler, reactnativeweb
Published: 2023-01-08 17:10:57

Many projects do not use a specific tool to manage multiple packages from the same repository, and this is not necessarily a problem. However, one of the trickiest issues in medium to large react-native projects is importing files outside of the root directory with Metro Bundler.

In fact, even in the React Native official documentation, you will not find a solution for this.

Consider the following project structure:

```
root
node_modules
src
  -- components
  -- views
  -- theme
  -- …others
web
  -- node_modules
  -- index.js
  -- …others
native
  -- node_modules
  -- android
  -- ios
  -- index.js
  -- App.jsx
  -- metro.config.js
  -- babel.config.js
  -- …others
```
This project uses react-native-web, so the code can be developed once and then run on web, iOS, and Android.

Let’s say you have an `Example` component in `src`.

```typescript
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

export const Example = () => {
 return (
   <View style={style.container}>
     <Text style={style.title}>Hello from Root: /src</Text>
   </View>
 );
};

const style = StyleSheet.create({
 container: {
   alignItems: 'center',
   backgroundColor: 'yellow',
   height: 200,
   justifyContent: 'center',
   width: 200,
 },
 title: {
   fontSize: 18,
 },
});
```

To import the `Example` component from `src` in your native `App.js`, you would do this:

```typescript
import {Example} from '../src/components';
```
Or, you could use an alias called `@components`:

```typescript
import {Example} from '@components';
```
However, the Metro Bundler would give you the following error:

```bash
BUNDLE  ./index.js 
error: Error: Unable to resolve module ../src/components from /…route_to_project_directory/rn-import-files-outside-root/native/App.js:
```


## Solution

- To solve this issue, you will need to modify the Metro configuration in your `native/metro.config.js` file:

```typescript
const path = require('path');

module.exports = {
 transformer: {
   getTransformOptions: async () => ({
     transform: {
       experimentalImportSupport: false,
       inlineRequires: true,
     },
   }),
 },
 resolver: {
   sourceExts: ['js', 'json', 'ts', 'tsx', 'jsx'],
 },
 watchFolders: [
   path.resolve(__dirname, '../node_modules'),
   path.resolve(__dirname, '../src'),
   path.resolve(__dirname, '..'),
 ],
 projectRoot: path.resolve(__dirname),
};
```

- Add the `babel-plugin-module-resolver` to your development dependencies. Navigate to your React Native project directory and run:
```
yarn add -D babel-plugin-module-resolver
```

- Then, modify your `native/babel.config.js` file as follows:

```typescript
module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: [
    [
      'module-resolver',
      {
        root: ['../src'],
        alias: {
          '@components': '../src/components',
          'react-native': './node_modules/react-native',
          react: './node_modules/react',
        },
      },
    ],
  ],
};
```
**Note:** the exact contents of this file will depend on the specific configuration of your project.

To see the changes, stop the Metro Bundler in your terminal and start it again with the reset cache flag:

```
npx react-native start --reset-cache
```
This should allow you to import files from outside the root directory in your React Native project.

Here are some additional resources that may be helpful:

- Configuring Metro (official documentation): https://facebook.github.io/metro/docs/configuration/
- Example repository: https://github.com/developerdanx/rn-import-files-outside-root