Photo by JESHOOTS.COM on Unsplash

How to setup module resolution and path aliases with React Native and TypeScript

Note: the article has been updated for the latest React Native version (0.64.0)

As your React Native app grows the codebase, it becomes more challenging to manage all the connections between the JavaScript and the 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, the path changes and 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 in 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, maintain, and clear to read with minimal effort.

Getting started

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

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

NOTE: The same steps are applicable to a project created 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 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:

module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
root: ['./src'],
extensions: [
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"]

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 using 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


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 --reset-cache

If the line above still doesn’t solve the issue, try more radical measures:

watchman watch-del-all && rm yarn.lock && rm -rf node_modules && rm -rf $TMPDIR/metro-* && rm -rf $TMPDIR/haste-map-* && yarn && yarn start --reset-cache

This 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.




Cross-platform Mobile Developer and React Native fan. I like clean code, Design Systems, new tech and implementing dark themes.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Display Layers provided in Mercator projection

Is it still worth using AngularJS in 2020?

How to design and build a carousel feature in VueJS

Liverpool Locks New Video

We Refactored 10K Lines of Code in Our Open Source React Project

Diagram of the article’s main point: Restructured code led to better code reuse and fewer lines.

Why I choose React?

Moving Airbnb Search to React

Browser dialog and notifications with Angular and Alertify JS

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Alex Fomushkin

Alex Fomushkin

Cross-platform Mobile Developer and React Native fan. I like clean code, Design Systems, new tech and implementing dark themes.

More from Medium

React State management with Providers

Authentication in React 18 using Firebase v9

Create dark mode structure in React projects (using Tailwind CSS, Typescript, and Custom Hook)

Dynamic fields in a form using react.js | react-native | add/delete input fields

Dynamic Fields in a form using react.js | react-native