How to setup pretty import paths in a create-react-app application
Tags: react, webpack, typescript, eslint
Published: 2021-07-05 17:00:01

## Pretty import paths?
We've all seen relative file import paths inside react applications. If you structure your apps like me you end up with terribly long paths to import other components. Auto-import can take care of that for you automatically, but isn't it hard to read and let's be honest, very, very error prone?

## Webpack aliases
One way to solve this issue is adding webpack aliases. Now if you created your application using the [`create-react-app` cli](https://www.npmjs.com/package/create-react-app), you will notice that there isn't a webpack config to mess with unless you [eject](https://create-react-app.dev/docs/available-scripts/#npm-run-eject) it running `npm run eject`, which will expose the entire configuration and makes you responsible of maintaining it. I prefer not ejecting my react application because I prefer the ease of use using react-scripts, so there must be another way.

## Introducing craco
[Create React App Configuration Override (CRACO)](https://www.npmjs.com/package/@craco/craco) offers a way of overriding or extending configurations like webpack for example. 

Bingo! 

Install it running the following command

```
npm i @craco/craco
```

Next we need to configure craco. We do so adding a craco configuration file. Create the file `craco.config.js` in the root of the project and add the following content

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

module.exports = {
  webpack: {
    alias: { '@': path.resolve(__dirname, './src') },
  },
};
```

Let me explain how I intend to use this alias. I usually have a `src/` folder in the root of the project containing all the components I use in a `components/` subfolder. Other folders are helpers under `helpers/` or custom hooks `hooks/`. The alias I am setting up will point to the `src/` folder. So whenever I write `import Component from '@/components/myComponent'` it will resolve to `'src/components/myComponent'`, independent of the path I am currently work in. 

The last thing to do is to run `craco` instead of `react-scripts` in our `package.json` scripts section:

```
{
  "scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test"
  }
}
```

This will load the craco config for you.

## ESLint
When using ESLint you will notice red squiggly line whenever you start using the new import paths. This is because ESLint doesn't know how to deal with those. I am also using the import plugin `eslint-plugin-import` to keep import order clean and tidy.

Inside your eslint config add the following block to the `settings` key.

```js
settings: {
  'import/resolver': {
    alias: {
      map: [['@', './src']],
      extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
    }
  },
},
```

The `alias` key here will tell ESLint about the alias key we've setup in our webpack config through craco. I also want to import the extensions listed above without typing out the extension, so that's what that part is for.

If you use the import plugin, don't forget to add that to the `extends` key:

```js
extends: [
  'plugin:react/recommended', 
  'plugin:import/recommended', 
  'plugin:import/typescript'],
```

I also use Typescript, see next section on how to add support for aliases.

Last thing which is entirely optional if you don't care about import order, is to tell the import plugin where we want to place the import statements using aliases. You do so by adding a configuration to the import rule:

```js
rules: {
  'import/order': [
    'error',
    {
      pathGroups: [
        {
          pattern: '@/**',
          group: 'parent',
          position: 'before',
        },
      ],
    },
  ],
},
```

This tells ESLint that all import paths matching the `pattern` key should be treated the same way as parent imports. Adding that last key `position` with value `'before' ` will move them over relative parent imports. You can read about what those keys do in the [official docs of eslint-plugin-import](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/order.md)

## Typescript (Bonus)
Finally if you are using typescript, we also need to set up alias support as the TS compiler will complain about the alias paths not being valid. 

For that open your `tsconfig.json` and add the following:

```json
{
  "compilerOptions": {
    "paths": { "@/*": ["./src/*"] },
  }
}
```

As mentioned before this maps paths like `@/*` to my `src/` folder.

## Conclusion
Completing the steps described above will give you cleaner import paths and reproducible and saner import order. You don't necessarily need the import order, but it's good practice and helps keeping your code more organized.