Create-React-App Path Aliases

2020-08-19 5 min

Trying to achieve the path aliasing in CRA projects keeping happy ESLint can be quite difficult. The reason why is because CRA does not allow to modify the internal configurations. Well, that’s not true at all. We can always eject the configuations and scripts and we’re free to customize settings.

But that’s not ideal. We don’t want to end up maintaining all the scripts and configurations by ourselves. The facebook guys do it really well by keeping all the internals in place.

There’s another solution before opening a CRA project. We can customize CRA settings by using react-app-rewired and customize-cra. I let to you checking on the docs to see how to install them.

Like everything else in life, there’s always mutiple ways to tackle a problem. Let’s check at two in particular to append path aliases.

Babel

The first method is letting babel to do the alias mapping. To get babel do the job, first we must install the babel-plugin-module-resolver plugin. You can see how to install in the docs.

Once installed, the next step is to configure the plugin in the .babelrc. We can also do the setup in the package.json using the babel key. You can read here the different ways to configure babel.

{
  "plugins": [
    ["module-resolver", {
      "alias": {
        "components": "./src/components",
        "helpers": "./src/helpers"
      }
    }]
  ]
}

By default, CRA is not going to read the newly created babel configuration. To do so, we must update the config-overrides.js provided by react-app-rewired and overriding the babel config by using the useBabelRc function provided by customize-cra. It should look like below.

const { override, useBabelRc } = require('customize-cra')
module.exports = override(
  useBabelRc()
)

That’s all for Babel. Now it’s the turn of ESLint.

ESLint

In order to make ESLint understand the alias set in babel, eslint-import-resolver-babel-module must be installed. But before doing so, make sure the eslint-plugin-import is also installed.

eslint-plugin-import is the ESLint plugin which checks for proper path imports.

Once everything is in its right place, we’re able to set the next options in the .eslintrc config file. Just like babel, ESLint can be setup in the package.json file using the eslint key.

{
  "settings": {
    "import/resolver": {
      "babel-module": {
        "alias": {
          "components": "./src/components",
          "helpers": "./src/helpers"
        }
      }
    }
  }
}

And that’s all. From now on we can use an alias to import components and helpers, without having ESLint whining like a whimsical child.

// Before
import { Alpha } from '../../../../components/Alpha'
// After
import { Alpha } from 'components/Alpha'

Jest

Wait! What happens when running the tests?

Cannot find module 'components/Alpha' from 'App.js'

This issue happens because Jest does not know about the mappings introduced by babel. Even while Jest uses babel to execute the test suite. What we must do to fix it, is to set again the alias mapping, but this time for Jest. So the Jest config should look like this.

"jest": {
  "moduleNameMapper": {
    "^components(.*)$": "<rootDir>/src/components$1",
    "^helpers(.*)$": "<rootDir>/src/helpers$1"
  }
}

And now it works. We can run the tests again.

Webpack

The other via, is to customize Webpack by using the resolve.alias feature.

But as you remember, we still have no access to configurations in a CRA folder. So, as the babel approach, let’s update the config-overrides.js file. In this case, we’re going to use the addWebpackAlias function provided by customize-cra.

const { addWebpackAlias, override } = require('customize-cra')
const path = require('path')

module.exports = override(
  addWebpackAlias({
    components: path.resolve(__dirname, './src/components'),
	helpers: path.resolve(__dirname, './src/helpers')
  })
)

With this setup, Webpack already knows how to resolve for components and helpers folders. Unlike the babel approach, this solution have a bit less boilerplate: instead of declaring a babelrc file and overriding the CRA’s babel setting, we just customize the Webpack setting in one place.

ESLint

In this case, because Webpack has beens used , we’re going to need another lib for ESLint: eslint-import-resolver-alias. And, as you’d expect, we have to map again the path locations.

{
  "settings": {
    "import/resolver": {
      "alias": {
        "map": [
          ["components", "./src/components"],
          ["helpers", "./src/helpers"]
        ],
        "extensions": [".js", ".jsx", ".json"]
      }
    }
  }
}

The extensions key can be omitted, so the configuration will look more succint.

{
  "settings": {
    "import/resolver": {
      "alias": [
        ["components", "./src/components"],
        ["helpers", "./src/helpers"]
      ],
    }
  }
}

And last but no least, Jest must be set up as seen in the example above. Mostly, because it is not using webpack nor Eslint settings.

Addendum

From CRA v3, there’s a thrid method to work with absolute imports (alias). According to official docs, we can set up a jsconfig.json file like in the next figure.

{
  "compilerOptions": {
    "baseUrl": "src"
  },
  "include": ["src"]
}

This new file must reside in the root folder next to the package.json.

And that’s pretty much it. No ESLint and Jest configurations.

I created a demo to test this approach and it seems working fine.

If the CRA project has been set up with Typescript, the same preferences must be placed in the tsconfig.json file.