DEV Community

Alba Moreno
Alba Moreno

Posted on • Updated on

Path Aliases with TypeScript in React

Have you ever found yourself typing so many ../../../ in typescript files? The deeper your project is the more typing is required to access modules in higher layers.

Paths Aliases to the rescue! They make the code look much cleaner. We can go from ugly to fancy paths/imports like so by declaring aliases that map to a certain absolute path in your application.

// Ugly path 😭
import { Button } from './../../../components/Button.tsx';

// Fancy path 🀩
import { Button } from '@components/Button.tsx';
Enter fullscreen mode Exit fullscreen mode

Setup

Imagine we have the following project structure:

src
β”œβ”€β”€ rest
β”‚Β Β  β”œβ”€β”€ complex-components
β”‚Β Β  β”‚Β Β  └── auth
β”‚Β Β  β”‚Β Β      └── UserAuth.tsx
β”‚Β Β  └── components
β”‚Β Β      β”œβ”€β”€ Button.tsx
β”‚Β Β      └── User.tsx
β”œβ”€β”€ services
β”‚Β Β  └── user-data-service.ts
└── setupTests.js
Enter fullscreen mode Exit fullscreen mode

Step 1: Install TS

Let's first install typescript in our project as a root dependency.

yarn add -D typescript
Enter fullscreen mode Exit fullscreen mode

Step 2: Configure Path Mappings

Next step is to create or update the tsconfig.json file with the path mapping. The TypeScript compiler supports the declaration of path mappings using paths property in tsconfig.json files.

{
  "compilerOptions": {
    "baseUrl": ".", // This must be specified if "paths" is.
    "paths": {
      "@...": ["..."] // This mapping is relative to "baseUrl"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

☝️ Please notice that paths are resolved relative to baseUrl. When setting baseUrl to a value other than ".", i.e. the directory of tsconfig.json, the mappings must be changed accordingly.

We are going to build our paths in a new file tsconfig.extend.json to make them more readable when importing and extend it from the tsconfig.json file. So,

  • Create the tsconfig.extend.json file at your project’s root.

  • Establish our baseUrl to be used as a reference.

{
  "compilerOptions": {
    "baseUrl": "./src"
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Define your paths mapping. We want typescript to import all files located in paths-to-search-for as @new-path-alias.
{
  "compilerOptions": {
    "baseUrl": ".", 
    "paths": {
      "@new-path-alias": ["paths-to-search-for"] 
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Like so:

{
    "compilerOptions": {
        "baseUrl": "./src",
        "paths": {
            "@components/*": [
                "./rest/components/*"
            ],
            "@complex-components/*": [
                "./rest/complex-components/*"
            ],
            "@services/*": [
                "./services/*"
            ]
        }
    }
}
Enter fullscreen mode Exit fullscreen mode
  • Extend the tsconfig.extend.json file from the tsconfig.json file:
{
  "compilerOptions": {
    ...
  },
  "extends": "./tsconfig.extend.json",
  "exclude": ["node_modules", "**/*.test.*", "build/**/*"]
}
,
Enter fullscreen mode Exit fullscreen mode

Now, you can use the new path aliases for module imports in your application. Let's run our code! πŸŽ‰

As soon as we run the code we get an error. What is going on here...? πŸ€”

Error: Can't resolve import '@complex-components/auth/UserAuth'
Enter fullscreen mode Exit fullscreen mode

A project created with create-react-app includes, in addition to React, other libraries like:

  • Webpack which is in charge of processing and packaging our JavaScript code (with its dependencies), CSS files and other static files such as images and fonts.
  • Babel which allows us to use new features of ECMAScript and JSX (JavaScript XML).
  • PostCSS which is a library for CSS processing.
  • Jest which is a library for testing.

Webpack it's also used to resolve imports so what we need to do is to modify that webpack configuration for it to resolve the alias we just defined in our tsconfig.json. There are multiple ways to achieve that, like:

  • Running npm run eject command: it's used to release all the tools behind Create React App, so that we can customize how it works. All the libraries mentioned remain hidden and cannot be customized. If your project needs a more specific configuration of any of these tools, you can launch the "eject" command, which brings all the hidden Create React App configuration out. Just keep in mind that the "eject" action cannot be undone!
  • Overriding Create React App configuration with CRACO. This is the most convenient way because we can override create-react-app configurations without the need to eject.

Step 3: Resolving alias with CRACO

First, we need to install CRACO

npm install --save @craco/craco
&&
npm install --save-dev craco-alias
Enter fullscreen mode Exit fullscreen mode

Then, create craco.config.js file at your project’s root (not inside src/) with the following configuration

const CracoAlias = require("craco-alias");

module.exports = {
    plugins: [
        {
            plugin: CracoAlias,
            options: {
                source: "tsconfig",
                // baseUrl SHOULD be specified
                // plugin does not take it from tsconfig
                baseUrl: "./src",
                // tsConfigPath should point to the file where "baseUrl" and "paths" are specified
                tsConfigPath: "./tsconfig.extend.json"
            }
        }
    ]
};
Enter fullscreen mode Exit fullscreen mode

The last thing is that we won’t be using react-scripts to start up our app anymore. We have to switch from react-scripts to craco to each of start, build, test and eject in package.json file.

"scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test",
    "eject": "craco eject"
  }
Enter fullscreen mode Exit fullscreen mode

Start React App with CRACO

Let's run our app

yarn start
Enter fullscreen mode Exit fullscreen mode

This time, our react app will not be throwing any errors for not being able to resolve our path alias!

Top comments (0)