Errors and fixes

Cannot find module after `shadcn add`: the aliases are the problem

The file is added, but TypeScript cannot find it. Usually the alias in components.json does not match tsconfig, or a runtime dependency is missing.

Key points

  • `components.json` and `tsconfig.json` must describe the same alias: with `@/` on one side and `~/` on the other, the file exists and TypeScript still cannot resolve it.
  • After `add`, check that the recipe dependencies were installed: the component compiles only when its imports exist.
  • In a monorepo the alias must point at `src`, not at the package root.

Short answer: the component was written, but the import uses an alias TypeScript does not know. Compare components.json with tsconfig.json: when the aliases disagree, the file exists and still fails to resolve.

Why the file exists and the compiler cannot see it

The two sides decide independently. The CLI writes the file where aliases says, meaning the spot chosen by the author of the recipe. TypeScript builds its program from include and rootDir, and resolves aliases from paths relative to baseUrl. When the file lands outside include, tsc never reads it and the error it prints is Cannot find module rather than not part of the project. The diagnosis starts from the wrong message, which is why the fix usually touches the alias while the problem was the scope of the project.

The four causes

CauseSymptomFix
Different alias in the two filesred import, file presentalign `aliases` and `paths`
Missing `baseUrl`every alias failsadd `baseUrl: "."`
Dependency not installederror on one packageinstall the listed dependencies
Monorepo with a root aliasworks in one package, not the otherpoint at the package `src`

Side-by-side comparison

// components.json
{
  "$schema": "https://ui.shadcn.com/schema.json",
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils",
    "ui": "@/components/ui"
  }
}
// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

If components.json says @/components and tsconfig maps @/* to ./src/*, the file lands in src/components and the import resolves. If either side uses ~/, the file is written in one place and looked for in another.

The check sequence

  1. Find the file that was just created: find . -name "button.tsx" -not -path "*/node_modules/*".
  2. Open components.json and write down the aliases.
  3. Open tsconfig.json and check that every alias has its own paths.
  4. Run npx tsc --noEmit: the error names the path it searched.

The command that shows where TypeScript looks

# ogni tentativo di risoluzione, con il percorso cercato
npx tsc --noEmit --traceResolution | grep "button"

# PowerShell
npx tsc --noEmit --traceResolution | Select-String "button"

The output is long and useful: for every import it lists the candidates in the order paths offers them, then closes with the line that says whether it resolved. Find the first not resolved line and read the path it tried. When the path is right and the file is not there, the CLI wrote elsewhere; when the path is wrong, the alias is wrong. Either way the output picks the config file to open instead of your instinct.

When the fix does not work

If the alias is correct and the error stays, the bundler resolver may be the problem: Vite and Next.js read paths differently, and some setups also need vite-tsconfig-paths. Check that the bundler config extends the same tsconfig. In a monorepo, a package can carry its own tsconfig and ignore the root one.

When the usual fix makes things worse

The fix found on forums is to add a second pattern to paths: "@/*": ["./src/*", "./*"], so every location works. It solves today's case and creates two valid sources for the same name. TypeScript takes the first that exists, the bundler may take the other, and the project starts compiling against an old copy of a component left in the root by an earlier init. The build passes, the page shows the wrong component, and the symptom resembles a cache. It is the one case on this list where the red goes away and the defect stays.

The limit of alignment

One limitation to declare: aligning the aliases fixes resolution, not the contract of the recipe. The CLI does not rewrite arbitrary imports inside the code it copies, so a third-party recipe may import a path that does not exist in your project, or exists under another name. The component compiles and a piece is missing. After every add the full check is two commands rather than one: the type check and opening the page.

How to prevent it

  • After every add, run tsc --noEmit in CI: a broken import never reaches production.
  • Keep one alias in the project: two conventions do not age well.
  • In a monorepo, add a test that creates an empty component and imports it from the consuming app.
  • One paths entry per alias: two candidates for the same name are a fault waiting to happen.

Where to continue

To understand the system: components, themes, blocks and newsletter block. The guides: free and premium themes and App Router structure; the comparison: private registries, tokens and namespaces.

Frequently asked questions

The file exists but the import is red: what do I check?

Open components.json, read aliases.components and aliases.utils, then compare them with compilerOptions.paths in tsconfig. Both must describe the same path.

Why does it work in Vite and not in Next.js?

The two have different default aliases and different tsconfig files. The recipe uses the alias declared in components.json, and falls back to the default when it is absent.

Do I have to restart the dev server?

For a new file, often yes, because the resolver caches paths. If the import is still red after a restart, the problem is configuration, not cache.

What does Missing dependency in the output mean?

The recipe imports a package the project does not have. Install what the command lists, then run it again.

Can I move the components out of src?

Yes, but update the aliases and the Tailwind content together. Moving only the files gives components that exist and lose their styles.