Path Mapping in TypeScript with Vite
Tags: react, typescript, vite, tutorial
Published: 2023-06-27 10:26:59

Working with imports in JavaScript applications can be a mess if you have deeply nested folder structures.

This can be even worse if your editor or IDE doesn't have intellisense or if it's not configured properly.

So you end up with some imports like this:

```jsx
// ❌
import { useForm } from "../../../../hooks/useForm";
import { TextInput } from "../../../form/TextInput";
import { Button } from "../../../ui/Button";
import { loginValidations } from "../../../../utils/validations";
import { routes } from "../../../../config/routes";
```

This will work but doesn't scale nor is readable to the developer.

## TypeScript Path Mapping

When using TypeScript it's very common to use [path mapping](https://www.typescriptlang.org/tsconfig#paths). This is useful because you can **omit the relative paths from the import and then get the module using an absolute import.**

```jsx
// ✅
import { useForm } from "@hooks/useForm";
import { TextInput } from "@components/form/TextInput";
import { Button } from "@components/ui/Button";
import { loginValidations } from "@utils/validations";
import { routes } from "@config/routes";
```

This can be achieved adding the `paths` config to your `tsconfig.json`:

```javascript
// tsconfig.json

{
  "compilerOptions": {
    "baseUrl": "./src", // This is to avoid adding the ./src prefix to each path below.
    "paths": {
      "@components/*": ["components/*"],
      "@hooks/*": ["hooks/*"],
      "@utils/*": ["utils/*"]
      "@config/*": ["config/*"]
    }
  }
}
```

This improves the readability a lot, and also if you move your components or modules to another folder, the chances that you need to update the imports are very low.

## Vite + TypeScript Path Mapping

If you try to create paths using Vite you'll notice that it's not possible, since **Vite doesn't know how to build the imports by default.**

There are two solutions to this issue: adding the paths manually to the Vite build config or using a third-party package.

### Adding paths manually

In order to add the paths manually to Vite build you'll need to configure all the [aliases](https://vitejs.dev/config/shared-options.html#resolve-alias) in `vite.config.ts`:

```typescript
// vite.config.ts

// ...
import path from "path";

export default defineConfig({
  // ...
  resolve: {
    alias: [
      {
        find: "@components",
        replacement: path.resolve(__dirname, "src/components"),
      },
      {
        find: "@utils",
        replacement: path.resolve(__dirname, "src/utils"),
      },
      {
        find: "@hooks",
        replacement: path.resolve(__dirname, "src/hooks"),
      },
      {
        find: "@configs",
        replacement: path.resolve(__dirname, "src/configs"),
      },
    ],
  },
});

// OR EVEN BETTER:

const aliases = ['components', 'utils', 'hooks', 'configs'];

export default defineConfig({
  // ...
  resolve: {
    alias: aliases.map(alias => (
      {
        find: `@${alias}`,
        replacement: path.resolve(__dirname, `src/${alias}`),
      },
    ))
  },
});
```

The downside of this approach is that every time the paths on `tsconfig.json` change you'll need to update the `vite.config.ts` too. This is not an ideal workflow.

### Using `vite-tsconfig-paths`

To solve that you can use a package called [vite-tsconfig-paths](https://github.com/aleclarson/vite-tsconfig-paths) which does all that boring and repetitive work.

It's very simple to use. All the configuration can be done in **2 steps:**

1. Install it as a dev dependency:

```bash
# npm
npm i vite-tsconfig-paths --save-dev

# yarn
yarn add -D vite-tsconfig-paths
```

2. Then add it as a Vite plugin:

```typescript
// vite.config.ts

import tsconfigPaths from "vite-tsconfig-paths";

export default defineConfig({
  plugins: [tsconfigPaths()],
});
```

And that's it! Every new path added will work without any changes needed.

---

If you have any feedback or suggestions, [send me an email](mailto:lucashwolff@gmail.com)

Great coding!