DEV Community

Cover image for Fix: compilerOptions.paths Must Not Be Set (Alias Imports)
Mahdi BEN RHOUMA
Mahdi BEN RHOUMA

Posted on Originally published at iloveblogs.blog

Fix: compilerOptions.paths Must Not Be Set (Alias Imports)

TL;DR

compilerOptions.paths must not be set (alias imports are not supported) is thrown by Create React App's build-time config validator, not by the TypeScript compiler itself — CRA explicitly disallows the paths field in tsconfig.json because its Babel-based build has no mechanism to resolve the aliases it defines. Three fixes exist, from smallest change to full migration.

  • Symptom: Build fails immediately on a project that otherwise compiles fine with tsc
  • Root cause: Create React App's react-scripts validates tsconfig.json and rejects paths outright — Babel strips types without resolving aliases
  • Fastest fix: Add CRACO to remap the same aliases into webpack's resolve.alias, no eject required
  • Longer-term fix: Aliases work natively, with zero extra config, on Vite and Next.js — both resolve tsconfig.json paths as part of their standard build

Why this only happens on Create React App

The Stack Overflow question "compilerOptions.paths must not be set (alias imports are not supported)" has 38,000+ views because the setup that triggers it is common: add path aliases to tsconfig.json for cleaner imports, and the build fails outright.

// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": "src",
    "paths": {
      "@/*": ["*"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
Failed to compile.

The following changes are being made to your tsconfig.json file:
  - compilerOptions.paths must not be set (aliased imports are not supported)
Enter fullscreen mode Exit fullscreen mode

The message is specific to react-scripts, the build tool behind Create React App. CRA compiles TypeScript by stripping types with Babel — it never runs the full TypeScript compiler as part of the build, only as a separate, parallel type-checking process. Babel has no concept of tsconfig.json's paths field; it transpiles each file independently without resolving module specifiers against a config map. If CRA silently allowed paths, import { Button } from '@/components/Button' would compile without error and then fail at runtime with a module-not-found error in the bundled output — so react-scripts fails loudly at build time instead, and (depending on version) may even rewrite your tsconfig.json to remove the field automatically.

Three ways to keep alias imports

1. CRACO — the fix that doesn't eject

CRACO (Create React App Configuration Override) lets you customize CRA's webpack config without running npm run eject, which is irreversible and hands you the full, unmaintained webpack config to own forever.

npm install -D @craco/craco
Enter fullscreen mode Exit fullscreen mode
// craco.config.js
const path = require('path');

module.exports = {
  webpack: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
    },
  },
};
Enter fullscreen mode Exit fullscreen mode
// package.json  swap react-scripts for craco in the three build scripts
{
  "scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test"
  }
}
Enter fullscreen mode Exit fullscreen mode

Your tsconfig.json can keep paths for editor autocomplete and tsc --noEmit type-checking — CRACO's webpack alias is what actually resolves the import at bundle time, so both tools need the mapping defined, just in different files.

2. react-app-rewired

An older, similarly-scoped alternative to CRACO, useful mainly if a project already depends on it:

npm install -D react-app-rewired
Enter fullscreen mode Exit fullscreen mode
// config-overrides.js
const path = require('path');

module.exports = function override(config) {
  config.resolve.alias = {
    ...config.resolve.alias,
    '@': path.resolve(__dirname, 'src'),
  };
  return config;
};
Enter fullscreen mode Exit fullscreen mode

CRACO has broader plugin support and more active maintenance as of this writing; prefer it for a new setup.

3. Migrate off Create React App

Create React App has had no new major release in years and no longer appears in React's own official "start a new app" recommendations. Both Vite and Next.js resolve tsconfig.json's paths natively — no CRACO, no config override, no eject:

// tsconfig.json  works as-is on Vite and Next.js, no extra tooling
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Vite additionally needs vite-tsconfig-paths if you're not using its React plugin's built-in resolution; Next.js resolves tsconfig.json paths out of the box with zero plugins, in both the App Router and Pages Router. If the project is a client-only SPA, Vite is the closer drop-in replacement for CRA; if it needs server rendering, API routes, or is already trending toward a full-stack app, migrating straight to Next.js avoids a second migration later.

Verifying the fix

  1. After adding CRACO (or react-app-rewired), run a clean build (npm run build) — the specific "paths must not be set" error should be gone, and the build should complete.
  2. Import something through the alias in a component that wasn't previously using it, to confirm resolution works in the bundled build, not just in the editor (which resolves tsconfig.json paths independently via the TypeScript language server regardless of what the bundler does).
  3. Run tsc --noEmit separately — this checks types using the same tsconfig.json, and should already have been passing even before the alias config was fixed for the bundler, since tsc itself never rejected paths.

Related Articles


Originally published at https://www.iloveblogs.blog

Top comments (0)