Skip to content

Avoid the Fear of Refactoring with Absolute Imports in React

Using absolute path aliases in React to make your imports refactor-friendly

As soon as I started developing in React, I noticed how easily refactoring can turn into a painful experience. Based on my expertise, this can break most of your imports.

If you have ever dealt with the fear of refactoring, then this tutorial is for you. Do not panic, because there is a solution!

In this tutorial, I will show you how to make your import statements way easier to refactor by using aliases, as shown below.

Before:

import UserCard from "../../components/UserCard";
import userDefaultImage from "../../../assets/user-default-image.png";
import { UserAPI } from "../../apis/UserAPI";
import _ from "lodash";

After:

import UserCard from "@components/UserCard";
import userDefaultImage from "@assets/user-default-image.png";
import { UserAPI } from "@apis/UserAPI";

As you can see, the proposed solution makes your imports look cleaner too, isn’t it? What a great side effect!

Let’s see how to import modules, API definitions, utilities, components, or assets with absolute path aliases in webpack and React.

Relative Imports vs Absolute Imports

By default, relative paths are the supported way of importing files in React. They are called relative because they use a path relative to the current file.

For small projects, they are a great solution. On the other hand, when it comes to large projects, dealing with relative imports can turn into a nightmare. In fact, in a deeply hierarchical directory structure, you might end up with relative imports looking like this:

import UserCard from "../../../components/UserCard";

Not only this does not look good, but relative imports are also affected by the following issues:

  • They make your codebase hard to refactor
  • They are not scalable to the size of your project
  • They make creating an npm module from your codebase complex

This is why you should use them sparingly. Ideally, only if you have closely related files that can be considered part of the same larger module. In any other case, absolute imports should represent your preferred approach. They are called absolute because they allow you to import files from absolute paths, unrelated to the current file. Thanks to them, you can turn the previous example into something like this:

import UserCard from "@components/UserCard";

Better, right? Let’s see how to achieve such a result.

Defining Absolute Path Aliases in webpack

First of all, you need to add react-app-rewired and react-app-rewire-alias to your project’s dependencies. You can install both with the following command:

npm install react-app-rewired react-app-rewire-alias --save

To make absolute imports work you have to change to your webpack configuration. If you created your app by using create-react-app, you might be forced to eject your project — and this should be avoided if not strictly required.

This is where react-app-rewired comes into play!

Please, note that if your project already uses react-app-rewired you can skip the following two steps.

Thanks to it, you can easily tweak the create-react-app webpack config. In order to make everything work, you need to follow the next two steps:

  1. Create a valid config-overrides.js file in your root directory
  2. Change the existing references from react-scripts to react-app-rewired in the scripts section of your package.json file

Before:

{ 
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  }
}

After:

{ 
  "scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-app-rewired eject"
  }
}

You have just rewired your React app, and you are now ready to declare your first absolute path aliases. Add the following lines to your config-overrides.js file:

const { alias } = require("react-app-rewire-alias")

module.exports = {
    webpack: (config, env) => {
         
        alias({
             // define these based on your needs
            "@components": "./src/components",
            "@assets" : "./src/assets",
            "@apis" : "./src/apis"
        })(config)

      return config
    }
  
  // ...
  
}

First, you need to import alias from react-app-rewire-alias. Then, you can call it by passing an object with key-value pairs. The keys represent your aliases and the values represent the corresponding paths.

Et voilà! You can now use the three aliases defined above to import the files located in those folders absolutely as follows:

import UserCard from "@components/UserCard";
import userDefaultImage from "@assets/user-default-image.png";
import { UserAPI } from "@apis/UserAPI";

Plus, react-app-rewired allows you to achieve other fantastic goals. For example, keeping one dedicated build folder per environment.

Bonus: Setting Up Your IDE to Deal with Absolute Paths

In order to make your IDE able to understand absolute paths, you need to do some extra operations.

IntelliJ IDEA

Create a webpack.config.alias.js file in your root folder and define it as follows:

const path = require('path');

module.exports = {
    resolve: {
        alias: {
             // define these based on your needs
            '@components': path.resolve(__dirname, 'src/components/'),
            "@assets" : path.resolve(__dirname, 'src/assets/'),
            "@apis" : path.resolve(__dirname, 'src/apis/'),
        }
    }
};

Then, File > Settings… > Languages & Frameworks > JavaScript > Webpack. Choose the Manually option and select the file defined above.

Visual Studio Code

Create a jsconfig.js and add the following lines to it:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
       // define these based on your needs
      "@components/*": ["src/components/*"],
      "@assets/*": ["src/assets/*"],
      "@apis/*": ["src/apis/*"]
    }
  },
  "exclude": ["node_modules"] 
}

Conclusion

Here we looked at how to use absolute path aliases in React. They are a great way to avoid the fear of refactoring since they do not lead to broken imports as opposed to relative imports. This is why you should always use them, and this can be easily be achieved as shown in the article.

Thanks for reading! I hope that you found this article helpful.

nv-author-image

Antonello Zanini

I'm a software engineer, but I prefer to call myself a Technology Bishop. Spreading knowledge through writing is my mission.View Author posts

Want technical content like this in your blog?