How to setup module resolution and path aliases with React Native and TypeScript
A detailed tutorial on how to enable module resolution in TypeScript to allow path aliases for clean and maintainable imports.
Alex · November 3rd, 2019 · 4 min read
Photo by JESHOOTS.COM on Unsplash

As your React Native app grows its codebase it becomes more challenging to manage all the connections between the JavaScript and TypeScript files. The more complex your folder structure evolves the harder it is to keep all the path tracking intact. Having deeply nested file structure can lead to some monsters like this:

import MyFancyButton from "../../../../components/common-components/MyFancyButton"

If you move the component and the path changes then you have to update all the affected files that use this module. And not to mention how ugly it looks if you are a perfectionist like me. If only there was a way to make your code look like this:

import MyFancyButton from "common-components/MyFancyButton"

Or even better if you configure an index.ts file into the desired folder:

import { MyFancyButton } from "common-components"

To achieve this eye-pleasing code all you need is a bit of configuration magic called module resolution/path aliases. Your code becomes easy to refactor and maintain and clear to read.

Getting started

For this tutorial, I'm going to use the latest React Native version available at the moment of writing this article: 0.61.3.

The demo app and the source code are available at GitHub.

NOTE: The same steps are applicable to a project initialized with Expo.

To initialize a fresh React Native project with TypeScript template type:

npx react-native init MyTypeScriptApp --template react-native-template-typescript

After the project was initialized and all the under the hood work is done we need to add a Babel plugin to enable the module resolution:

yarn add --dev babel-plugin-module-resolver

Or if you use npm:

npm install --save-dev babel-plugin-module-resolver

After the plugin was installed we need to update babel.config.js. Add 'module-resolver' to the list of Babel plugins:

  presets: ['module:metro-react-native-babel-preset'],
  plugins: [
    [
      'module-resolver',
      {
        root: ['./src'],
        extensions: [
          '.ios.ts',
          '.android.ts',
          '.ts',
          '.ios.tsx',
          '.android.tsx',
          '.tsx',
          '.jsx',
          '.js',
          '.json',
        ],
        alias: {
          screens: './src/screens',
          navigation: './src/navigation',
          components: './src/components',
          'common-components': './src/common-components',
          state: './src/state',
        },
      },
    ],
  ],
}

'root' specifies your project main directory. Usually, it is called 'src'.

'extensions' allow you to limit the plugin to specific file types.

'alias' lets you specify all the folders you need shortcuts for your module imports.

NOTE: You should use full paths for your alias folders otherwise the plugin won't be able to locate the folders you specified.

The first part of the configuration is done and now it is time to configure the TypeScript configuration in tsconfig.json:

{
  "compilerOptions": {
    ...
    "baseUrl": "./src",
    /* Base directory to resolve non-absolute module names. */
    "paths": {
      "screens": ["./screens"],
      "components": ["./components"],
      "navigation": ["./navigation"],
      "common-components": ["./common-components"],
      "state": ["./state"]
    },
    /* A series of entries which re-map imports to lookup locations
    relative to the 'baseUrl'. */
    ...
  },
  ...
}

As we did before you need to specify 'baseUrl' for your main work directory and provide paths for your module shortcuts.

NOTE: Even though you don't need to specify full paths in tsconfig.json file please remember to put the paths into square brackets []. Otherwise, TypeScript won't be able to locate the paths properly.

NOTE: To save yourself the trouble of editing all the paths manually I recommend to use automatic tooling available for the IDE of your choice. If you use WebStorm like me, you can find the setting in Editor/Code Style/TypeScript/Use paths mappings from tsconfig.json

Troubleshooting

If you followed the previous steps and still have issues it might be related to old cache still being used to resolve file pathing. If that happens make sure to start your React Native project with:

yarn start -c

If that still doesn't solve the issue, try more radical measures:

watchman watch-del-all && rm -rf $TMPDIR/react-* && rm -rf
node_modules/ && npm cache verify && npm install &&
npm start -- --reset-cache

That should solve the issue if your setup and pathing are correct in both babel.config.js and tsconfig.json and the 'babel-plugin-module-resolver' is installed correctly. 


After the setup is done you can start enjoying the neat module imports with path aliases. Now you can either reformat your code all at once or one line at a time.

And you can always read more about the module resolution for TypeScript in the official docs.

This is my first ever blog post 🥳 so let me know if you found it useful or had some issues not covered by the article.

Tags: React Native, TypeScript