How to setup pretty import paths in a create-react-app application

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, you will notice that there isn't a webpack config to mess with unless you 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

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

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.

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:

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:

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

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:

{
  "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.

21