DEV Community

Cover image for How to setup pretty import paths in a create-react-app application
Chris
Chris

Posted on

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

Create React App Configuration Override (CRACO) offers a way of overriding or extending configurations like webpack for example.

Bingo!

Install it running the following command

npm i @craco/craco
Enter fullscreen mode Exit fullscreen mode

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') },
  },
};
Enter fullscreen mode Exit fullscreen mode

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"
  }
}
Enter fullscreen mode Exit fullscreen mode

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'],
    }
  },
},
Enter fullscreen mode Exit fullscreen mode

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'],
Enter fullscreen mode Exit fullscreen mode

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',
        },
      ],
    },
  ],
},
Enter fullscreen mode Exit fullscreen mode

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/*"] },
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (5)

Collapse
 
avxkim profile image
Alexander Kim • Edited

This CRACO package doesn't work with latest CRA 5 (webpack 5). Any other alternatives?

Collapse
 
aliraza profile image
Ali Raza

use 6.1.2 version

Collapse
 
ultralabsgev profile image
ultralabsgev

can someone pls explain to me why i still getting error when i'm using

import SomeComponent from '@baseComponents/SomeComponent';

this is my error
 Unable to resolve path to module '@baseComponents/SomeComponent'
Enter fullscreen mode Exit fullscreen mode

this is part of my esLint

"settings": {
    "import/resolver": {
      "alias": {
        "map": [["@", "./src"], ["@baseComponents", "./src/components/Base/*"]],
        "extensions": [".ts", ".tsx", ".js", ".jsx", ".json"]
      },
    }
  }
Enter fullscreen mode Exit fullscreen mode

and this is part of my craco.config

  webpack: {
    alias: {
      '@convertor': path.resolve(__dirname, `src/convertors`),
      '@baseComponents': path.resolve(__dirname, `src/components/Base`),
    },
  },
  jest: {
    configure: {
      moduleNameMapper: {
        '^@baseComponents(.*)$': '<rootDir>/src/components/Base',
      },
    },
  },
Enter fullscreen mode Exit fullscreen mode

this is tsconfig.paths.json

{
  "compilerOptions": {
    "baseUrl": "src",
    "paths": {
      "@baseComponents/*": [ "src/components/Base/*" ],
      "@convertor/*": [ "src/convertors/*" ],
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
valcho profile image
Valentin Ganchev

So this works at your end and the "paths" property in the compilerOptions in tsconfig.json doesn't get deleted?

Collapse
 
emmanuelonah profile image
Emmanuel Onah

Mine get deleted, the paths gets deleted when i start the app.

This doesn't work for me unfortunately.