Shadcn UI 'Component Not Found' After Installation - Fixed (2025 Guide)
Shadcn component not found after install? This guide covers path alias fixes, components.json configuration, TypeScript mapping errors, and when to escalate beyond DIY debugging.
FlowQL Team
AI Search Optimization Experts
You just ran npx shadcn-ui@latest add button, watched the CLI cheerfully confirm "Component installed successfully," and now your import is failing with "Module not found: Can't resolve '@/components/ui/button'." The component files exist. Your IDE even autocompletes the path. But your build is broken.
This is Shadcn's dirty secret. The promise is beautifully simple: copy-paste UI components, no npm packages, no dependency hell. Just run the CLI and start building. But when that promise breaks, you're not debugging React components anymore. You're debugging the invisible configuration layer where components.json, tsconfig.json, and your bundler all need to agree on what @/components actually means.
AI tools can't help you here. They see your import statement and suggest "did you install the component?" - which you did. They can't see the path alias mismatch between what the CLI thinks and what TypeScript resolves. You're stuck in the gap between "copy-paste UI" and "understanding module resolution."
This guide walks through the real fixes: aligning path aliases across configuration files, diagnosing why components install but don't import, understanding the difference between IDE resolution and build resolution, and knowing when configuration debugging exceeds DIY territory. We'll cover Next.js App Router quirks, Vite alias setup, monorepo path mapping, and the framework-specific gotchas that make Shadcn imports fail.
Understanding the Problem: When 'Copy-Paste' Components Fail
Before jumping into fixes, let's get precise about what "component not found" actually means. This isn't pedantic. The specific error message tells you which layer is broken.
What Does 'Component Not Found' Actually Mean in Shadcn?
The error appears in three distinct forms, each pointing to a different failure point. The most common: Module not found: Can't resolve '@/components/ui/button' during build. This means your bundler (webpack, Vite, Turbopack) doesn't understand the @/ path alias that Shadcn's CLI generated.
Another variant: Cannot find module '@/components/ui/button' or its corresponding type declarations as a TypeScript error. This means TypeScript's path mapping isn't configured to match what components.json expects. Your bundler might be fine, but tsc can't verify the import.
The most deceptive version: your IDE shows green checkmarks, autocomplete works perfectly, but npm run build fails with module resolution errors. This happens when your IDE's TypeScript server reads tsconfig.json correctly, but your bundler uses a different path resolution strategy that ignores TypeScript's configuration.
All three scenarios share the same root cause: configuration mismatch. The Shadcn CLI installed components to a directory, recorded that path in components.json, but the rest of your toolchain doesn't know where to look.
The Configuration Layer: Where Shadcn's Promise Breaks
Shadcn's appeal is that it's not a component library in the traditional sense. It doesn't publish to npm. You don't add shadcn-ui to your dependencies. Instead, the CLI copies component source code directly into your project. You own the code. You can modify it. No black box.
This architecture is elegant until you realize it depends entirely on path aliases working correctly. Traditional component libraries like Material UI or Chakra handle path resolution for you - you import from @mui/material and npm/bundler does the rest. With Shadcn, you're responsible for configuring the path from @/components/ui/button to ./components/ui/button.tsx (or wherever you told the CLI to put files).
The configuration layer has three pieces that must align: components.json tells Shadcn where to install files, tsconfig.json tells TypeScript how to resolve @/ imports, and your bundler configuration (Next.js auto-handles this, but Vite/webpack need explicit setup) tells the build tool how to translate @/ to actual file paths.
When any one piece is out of sync, imports fail. The CLI "successfully" installed the component, but to the wrong location. Or it installed to the right location, but TypeScript isn't configured to find it. Or both are correct, but your bundler doesn't respect TypeScript's path mappings.
This is the invisible layer that AI tools can't diagnose. They see your code, not your configuration. They can't compare components.json against tsconfig.json against vite.config.ts to spot the mismatch.
Visual Guide: Successful vs Failed Shadcn Import
A successful Shadcn setup shows consistent paths across all configuration. If you run cat components.json, you see "components": "@/components". If you check tsconfig.json, you see "paths": { "@/*": ["./*"] } with "baseUrl": ".". If you run find . -name button.tsx, the file exists at ./components/ui/button.tsx. Everything aligns.
A broken setup shows mismatches. Maybe components.json says "@/components" but tsconfig.json has "paths": { "@/*": ["./src/*"] } with baseUrl pointing to a different directory. Or components.json uses ~/components but you never configured the ~ alias anywhere. Or the component installed to src/components/ui/button.tsx but your path mapping expects components/ui/button.tsx at the root.
The error message often doesn't reveal which configuration is wrong. It just says "can't find module" because from the build tool's perspective, the path doesn't exist. You have to manually verify that all three systems (CLI, TypeScript, bundler) are speaking the same language.
Root Causes: Why Shadcn Components Can't Be Found
Every "component not found" error traces back to one of six root causes. Identifying which one you're hitting determines the fix.
Path Alias Mismatch Between components.json and tsconfig.json
This is the single most common failure mode. When you run npx shadcn-ui init, the CLI asks "Configure the import alias?" with a default of @/*. If you accept that default, Shadcn writes "components": "@/components" to components.json. It assumes your project already has @/* configured in TypeScript.
But if you're initializing Shadcn in a fresh Create React App, a bare Vite project, or any setup that doesn't pre-configure the @ alias, TypeScript has no idea what @/components means. The CLI doesn't modify tsconfig.json for you. It just records what you told it to use.
The fix requires manually adding path mapping to tsconfig.json. But here's the gotcha: the path needs to account for baseUrl. If you set "baseUrl": "." and "paths": { "@/*": ["./*"] }, that means @/components maps to ./components relative to your project root. If your components are actually in src/components, you need either "paths": { "@/*": ["./src/*"] } or you need to move the components directory.
This failure presents as TypeScript errors in your editor and build-time module resolution errors. Both IDE and bundler are confused because the path mapping is missing or wrong.
Incorrect Component Installation Directory
Shadcn's CLI installs components wherever components.json specifies, but it doesn't validate that location makes sense for your project structure. If you told it to use @/components but your Next.js project uses App Router with everything in app/, you've now got components at components/ui/button.tsx when they should be at app/components/ui/button.tsx.
Next.js conventions matter here. Pages Router typically has code at the root (components/, pages/, lib/). App Router puts everything under app/ (app/components/, app/lib/, app/[routes]). The Shadcn UI official installation guide provides separate instructions for each, but if you mix them, imports break.
Vite projects usually put source code in src/, so components should go in src/components/ui/. If you initialize Shadcn with @/components and the CLI creates components/ui/ at the root instead, your imports fail because Vite's default setup expects everything in src/.
The symptom: the component files exist, but not where the path alias is looking for them. Running find . -name button.tsx shows the file at ./components/ui/button.tsx, but your imports expect ./src/components/ui/button.tsx.
Missing or Misconfigured TypeScript Path Mappings
Even if components.json and your directory structure align, TypeScript's compilerOptions.paths needs exact configuration to work. This is where subtle errors hide.
The baseUrl setting is required for paths to work at all. If you define "paths": { "@/*": ["./*"] } without setting "baseUrl": ".", TypeScript ignores the path mapping entirely. The TypeScript path mapping documentation makes this dependency clear, but it's easy to miss.
Another gotcha: relative paths in paths are relative to baseUrl, not to the tsconfig.json file location. If you have "baseUrl": "./src" and "paths": { "@/*": ["./*"] }, the @/ alias maps to ./src/*, not to the project root. If Shadcn installed components to ./components (outside src/), your imports fail.
TypeScript also supports arrays of paths as fallbacks: "paths": { "@/*": ["./src/*", "./*"] }. This tells TypeScript "try src/ first, then fall back to root." This can mask configuration issues by making imports work in the IDE while builds fail if your bundler doesn't support the same fallback logic.
Case sensitivity is another trap. On macOS and Windows, file systems are case-insensitive by default, but Linux is case-sensitive. If your tsconfig.json says "@/*": ["./Components/*"] but the directory is actually components/ (lowercase), it works locally but breaks in CI/CD running on Linux.
Case Sensitivity Issues (Windows vs Mac vs Linux)
This failure mode is especially frustrating because it works on your machine but fails for teammates or in deployment. If you develop on macOS or Windows where file systems default to case-insensitive, you can import from @/Components/ui/button even though the directory is actually components/ (lowercase). The OS doesn't care.
But when your code deploys to a Linux server (which most hosting platforms use), suddenly the import breaks. The file system demands exact case matching. The Stack Overflow Shadcn questions are full of "worked locally, breaks in production" issues caused by this.
Shadcn's CLI creates directories in lowercase by default (components/ui/, not Components/UI/), so if you follow the defaults, you shouldn't hit this. But if you manually renamed directories or if you're mixing Shadcn with existing code that uses different casing conventions, the mismatch causes problems.
The fix: enforce consistent lowercase paths everywhere. Update components.json, tsconfig.json, and all import statements to use the exact casing that actually exists in your file system. Test on a case-sensitive system (or use Docker with a Linux container) before deploying to catch these issues early.
Bundler-Specific Module Resolution Problems (Vite vs webpack)
TypeScript's path mappings configure type checking and IDE autocomplete, but they don't automatically configure your bundler. This is where Next.js users have it easy - Next.js reads tsconfig.json and automatically applies the same path aliases to webpack/Turbopack. But Vite and raw webpack require explicit configuration.
Vite projects need path aliases defined in vite.config.ts using the resolve.alias option. Even if your tsconfig.json is perfect, Vite doesn't read it for module resolution. You need this setup:
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});
Notice the path mapping is different from TypeScript: Vite needs the full resolved path using path.resolve(), not a relative string. This is one more place for configuration to get out of sync.
Webpack 5 projects (if you're configuring webpack yourself outside Next.js) need similar setup using resolve.alias. Turbopack in Next.js 13+ reads tsconfig.json automatically, but you can also override in next.config.js if needed.
The symptom: TypeScript shows no errors, your IDE autocomplete works, but npm run build fails with "Module not found." Your IDE is using TypeScript's path resolution (which is configured), but the bundler is using its own resolution (which isn't).
Incomplete Shadcn Initialization (Missing components.json)
If you installed Shadcn components without first running npx shadcn-ui@latest init, the CLI created component files but didn't create components.json. Without that configuration file, reinstalling or updating components won't work correctly because Shadcn doesn't know your project's conventions.
This typically happens when someone copies Shadcn components from another project or downloads them manually instead of using the CLI. The components might even work initially if you use relative imports (import { Button } from '../components/ui/button'), but as soon as you try to add more components via CLI, things break.
The fix: Run npx shadcn-ui@latest init even if components already exist. The CLI will detect the existing setup and ask if you want to overwrite. Choose your preferred configuration (TypeScript, CSS location, import alias) and let it create components.json. Then reinstall components to ensure they match the new configuration.
This root cause is less common than path alias mismatches, but when it happens, the symptoms are confusing because some components work (the ones you copied manually) while others fail (the ones the CLI tries to install).
Quick Diagnosis: Verify Your Shadcn Installation (3-Minute Check)
Before attempting fixes, run through this diagnostic checklist. It takes three minutes and tells you exactly which configuration layer is broken.
Step 1: Confirm components.json Exists and Is Valid
Run this in your terminal at the project root:
# Check if components.json exists
cat components.json
# You should see output like:
# {
# "$schema": "https://ui.shadcn.com/schema.json",
# "style": "default",
# "rsc": true,
# "tsx": true,
# "tailwind": { ... },
# "aliases": {
# "components": "@/components",
# "utils": "@/lib/utils"
# }
# }
If cat components.json says "No such file or directory," you skipped initialization. Run npx shadcn-ui@latest init before proceeding.
If the file exists, verify the aliases section matches what you expect. If it says "components": "@/components" but you thought you were using ~/components, that's your first clue.
Note the exact alias string (including whether it has a trailing slash or not). This needs to match exactly in your TypeScript configuration.
Step 2: Check Component Files Actually Exist
Verify the CLI actually created component files where components.json expects:
# Find all Shadcn UI component files
find . -name "*.tsx" -path "*/ui/*" -type f
# If you installed button, specifically check:
ls -la components/ui/button.tsx
# or
ls -la src/components/ui/button.tsx
# Verify component exports
cat components/ui/button.tsx | grep "export"
If find returns no results, the components weren't installed. If the path doesn't match what components.json specifies under aliases.components, you've found the mismatch.
Check both components/ui/ at the root and src/components/ui/ if you're using Vite. The CLI might have installed to the wrong location based on how you answered the initialization prompts.
Step 3: Verify Path Alias Configuration Matches
Now cross-check TypeScript configuration against what components.json expects:
# Check TypeScript path configuration
cat tsconfig.json | grep -A 5 '"paths"'
# You should see something like:
# "paths": {
# "@/*": ["./*"]
# }
# Also verify baseUrl is set
cat tsconfig.json | grep "baseUrl"
The path mapping in tsconfig.json must match the alias in components.json. If components.json says @/components but tsconfig.json has no @/* mapping, that's your problem.
Common mismatch patterns:
components.jsonuses@/buttsconfig.jsonuses~/components.jsonexpects root-level components buttsconfig.jsonmaps@/*tosrc/*baseUrlis missing or points to the wrong directory
For Vite projects, also check vite.config.ts:
cat vite.config.ts | grep -A 3 "alias"
You need both TypeScript and Vite configured with matching paths.
Step 4: Test Import Resolution in Your IDE
Open a file in your IDE and try importing a Shadcn component:
// Create a test file: test-import.tsx
import { Button } from "@/components/ui/button";
export default function Test() {
return <Button>Test</Button>;
}
Watch how your IDE responds. If it shows a red squiggle under the import path, TypeScript can't resolve it - your tsconfig.json is misconfigured. If the import is green and autocomplete works, TypeScript is correct but your bundler might still be wrong.
Try Cmd/Ctrl+Click on the import path. If your IDE jumps to the component file, path resolution is working in the IDE. If it does nothing or shows an error, the path alias isn't configured.
This test separates IDE resolution (TypeScript) from build resolution (bundler). Both need to work for Shadcn to function.
Step 5: Check Build Output for Module Errors
Finally, attempt a build to see bundler-level errors:
# Next.js
npm run build 2>&1 | grep -i "module\|error"
# Vite
npm run build 2>&1 | grep -i "module\|error"
# Look for errors like:
# "Module not found: Can't resolve '@/components/ui/button'"
# "Failed to resolve import '@/components/ui/button'"
If TypeScript compilation passes but the build fails on module resolution, your bundler isn't configured to match TypeScript's paths. This is the Vite-specific issue mentioned earlier.
If both TypeScript and the build pass but runtime errors occur, you might have a different issue (missing dependencies for the component, Tailwind not configured, etc.) rather than path resolution problems.
This five-step diagnostic takes three minutes and pinpoints exactly which layer is broken. Now you know whether to fix components.json, tsconfig.json, bundler configuration, or file locations.
Solution 1: Fix Path Alias Configuration
If the diagnostic revealed mismatched path aliases, this is your fix. We'll align components.json, tsconfig.json, and bundler configuration to all speak the same language.
Aligning components.json with tsconfig.json Paths
Start by deciding on a canonical path alias. The Shadcn default is @/* mapping to your project root or src/ directory. Stick with this unless you have a strong reason to use a different convention.
Open components.json and verify the aliases section:
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "app/globals.css",
"baseColor": "slate",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}
This configuration tells Shadcn's CLI: "When I install a button component, put it at <project-root>/components/ui/button.tsx and generate import statements like import { Button } from '@/components/ui/button'."
If your components are in src/components/, change the alias to "components": "@/components" and ensure your TypeScript and bundler map @/ to ./src/.
⚠️ Warning: Warning: If you change the
componentsalias incomponents.jsonafter already installing components, existing imports won't automatically update. You'll need to either manually update import statements or reinstall components.
Configuring baseUrl and paths Correctly
Now update tsconfig.json to match. The critical settings are compilerOptions.baseUrl and compilerOptions.paths:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./*"]
}
// ... other options
}
}
This configuration says: "The @/ alias maps to the project root (.). An import from @/components/ui/button resolves to ./components/ui/button.tsx relative to baseUrl."
For Vite projects with code in src/, use:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
This maps @/components to ./src/components. Make sure components.json still uses @/components (not @/src/components) - the alias already includes the src/ part in the path mapping.
For monorepos or projects with multiple source directories, you can use fallback paths:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*", "./*"]
}
}
}
TypeScript tries ./src/* first, then falls back to ./* if the module isn't found. Be careful with this approach in bundlers that don't support path fallbacks - it can make TypeScript work while builds still fail.
Next.js-Specific Path Resolution Settings
Next.js automatically respects tsconfig.json paths in webpack/Turbopack, so usually no additional configuration is needed. But if you're using Next.js 13+ with App Router and seeing path issues, verify your directory structure matches the Shadcn components documentation.
For App Router, components typically live at app/components/ui/ or at the root components/ui/. If you chose App Router during shadcn init, the CLI should have configured correctly. If you migrated from Pages Router, you might have stale paths.
Check next.config.js for any webpack.resolve.alias overrides that might conflict:
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
// If you have custom webpack config, verify aliases don't conflict
webpack: (config) => {
config.resolve.alias = {
...config.resolve.alias,
// Make sure this matches your tsconfig.json
'@': path.resolve(__dirname, './'),
};
return config;
},
};
module.exports = nextConfig;
In most cases, you can omit custom webpack configuration and let Next.js read from tsconfig.json. Only add explicit alias configuration if you have complex module resolution needs.
Vite Projects: Setting Up Alias in vite.config.ts
Vite requires explicit alias configuration because it doesn't automatically read TypeScript paths. Add this to vite.config.ts:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});
This configuration must match your tsconfig.json paths. If TypeScript maps @/* to ./src/*, Vite must map @ to ./src. If TypeScript maps to project root, Vite maps to ./.
Note that Vite uses path.resolve(__dirname, ...) to get an absolute path. Don't use relative strings like './src' - while it might work in some cases, path.resolve ensures consistent cross-platform behavior.
For projects not using src/ directory (components at root), update the alias:
resolve: {
alias: {
'@': path.resolve(__dirname, './'),
},
},
After updating vite.config.ts, restart the Vite dev server. Configuration changes don't hot-reload.
Testing Path Resolution After Configuration
After aligning all configuration files, test that resolution works at every layer:
# Verify TypeScript recognizes the paths
npx tsc --showConfig | grep -A 5 'paths'
# Clear any stale caches
rm -rf .next # Next.js
rm -rf node_modules/.vite # Vite
rm -rf dist # Build output
# Restart dev server
npm run dev
# Test build
npm run build
Create a test component that imports from Shadcn:
// pages/test.tsx or app/test/page.tsx
import { Button } from "@/components/ui/button";
export default function TestPage() {
return <Button>Path resolution works!</Button>;
}
If this component compiles and runs without errors, path aliases are correctly configured. If you still see module resolution errors, double-check that components.json, tsconfig.json, and bundler config all use identical alias conventions.
Pro Tip: Use consistent path aliases across all configuration files. The
@/convention is widely adopted and works well with most tooling. Avoid mixing@/,~/, and@@/in the same project.
Solution 2: Reinstall Shadcn Components Correctly
If path configuration looks correct but components still don't import, the issue might be that files were installed to the wrong location initially. Reinstalling with explicit configuration fixes this.
Reinitializing Shadcn UI (npx shadcn-ui@latest init)
Remove the existing components.json and reinitialize to ensure clean configuration:
# Backup existing components.json (in case you need to reference it)
mv components.json components.json.backup
# Reinitialize with interactive prompts
npx shadcn-ui@latest init
# Answer prompts based on your project:
# - Would you like to use TypeScript? › Yes
# - Which style would you like to use? › Default
# - Which color would you like to use as base color? › Slate
# - Where is your global CSS file? › app/globals.css (or src/index.css for Vite)
# - Would you like to use CSS variables for colors? › Yes
# - Are you using React Server Components? › Yes (Next.js App Router) / No (Vite)
# - Configure the import alias for components? › @/components
# - Configure the import alias for utils? › @/lib/utils
The critical prompt is "Configure the import alias for components?" - make sure this matches your tsconfig.json and bundler configuration. If your setup uses @/* mapped to ./src/*, answer @/components (the CLI will install to src/components).
After reinitialization, check that the new components.json matches your expectations:
cat components.json
Verify the aliases.components value matches your TypeScript paths exactly.
Removing Broken Component Files
Before reinstalling components, remove any existing files that might have been installed to the wrong location:
# Find all component files
find . -name "*.tsx" -path "*/ui/*" -type f
# Remove the entire ui directory if it's in the wrong place
rm -rf components/ui # If they should be in src/components
rm -rf src/components/ui # If they should be at root
# Also remove any manually created barrel exports
rm components/index.ts 2>/dev/null
rm src/components/index.ts 2>/dev/null
⚠️ Warning: Warning: This deletes component files. If you've customized Shadcn components with project-specific styling or behavior, back up those changes before removing files. You'll need to reapply customizations after reinstallation.
The goal is to start from a clean state where the CLI can install components to the correct location without conflicting with old files.
Installing Components with Explicit Path Override
Now reinstall components using the CLI. Start with a simple component like button to verify the setup:
# Install button component
npx shadcn-ui@latest add button
# Verify installation location
ls -la components/ui/button.tsx
# or
ls -la src/components/ui/button.tsx
# Check the component file
cat components/ui/button.tsx | head -20
The component file should import utilities using the same alias pattern:
// Expected imports in button.tsx
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils" // Should match your configured alias
If the component file shows import { cn } from "@/lib/utils" but your alias is configured differently (e.g., ~/lib/utils), the CLI didn't pick up your configuration correctly. This usually means components.json exists but the CLI is ignoring it - check that you're running the latest Shadcn CLI version.
Verifying Installation Success
Test the newly installed component in a real file:
// Create test-component.tsx in your appropriate directory
import { Button } from "@/components/ui/button";
export default function TestComponent() {
return (
<div className="p-4">
<Button variant="default">Click me</Button>
<Button variant="destructive">Delete</Button>
<Button variant="outline">Cancel</Button>
</div>
);
}
Run your dev server and verify:
- No TypeScript errors in your IDE
- Autocomplete works for
variantprop - Component renders in the browser
npm run buildcompletes without module resolution errors
If all four criteria pass, reinstallation succeeded. If you still see import errors, the issue is in bundler configuration, not component installation - revisit Solution 1.
Solution 3: Manual Import Path Fixes (When CLI Fails)
Sometimes the CLI's automatic path alias setup doesn't match your project's needs, or you're working in an environment where aliases don't work reliably. Manual import paths are a viable workaround.
Converting Absolute Imports to Relative Imports
Instead of fighting with path aliases, you can use relative imports from any file to the components directory:
// Before (using path alias)
import { Button } from "@/components/ui/button";
// After (relative path from pages/index.tsx)
import { Button } from "../components/ui/button";
// After (relative path from app/dashboard/page.tsx)
import { Button } from "../../components/ui/button";
This approach sacrifices the convenience of absolute imports, but it works universally without any bundler or TypeScript configuration. Every tool understands relative paths.
The tradeoff: refactoring becomes harder because moving a file requires updating all its imports. With absolute imports via aliases, you can move files freely as long as the components directory stays in place.
Use relative imports as a temporary workaround while you diagnose configuration issues, not as a permanent solution. If your team's entire codebase uses path aliases, switching Shadcn components to relative imports creates inconsistency.
Using Direct File Paths Instead of Barrel Exports
Shadcn components are self-contained files without barrel exports (no components/ui/index.ts re-exporting everything). This means you always import directly from the component file:
// Correct - direct file import
import { Button } from "@/components/ui/button";
import { Card, CardHeader, CardTitle } from "@/components/ui/card";
// Wrong - trying to use non-existent barrel export
import { Button, Card } from "@/components/ui"; // This file doesn't exist
If you're getting "module not found" errors on imports that look correct, verify you're not trying to import from a directory path expecting a barrel export. Shadcn doesn't create index.ts files.
Some developers add their own barrel exports for convenience:
// components/ui/index.ts (manually created)
export * from "./button";
export * from "./card";
export * from "./input";
// etc.
This works, but it's not part of the Shadcn convention and you'll need to maintain it manually when adding components. The official approach is direct imports from each component file.
Debugging IDE vs Build Import Resolution Differences
Your IDE (VS Code, WebStorm, etc.) uses TypeScript's language server for import resolution, while your build uses webpack/Vite/Turbopack. They can disagree on whether an import is valid.
If imports show green in your IDE but fail at build time:
- Your
tsconfig.jsonis correct (IDE works) - Your bundler config is missing or wrong (build fails)
- Solution: Add path aliases to
vite.config.tsornext.config.jsas shown in Solution 1
If imports show red in your IDE but build succeeds:
- Your bundler config is correct (build works)
- Your
tsconfig.jsonis missing paths (IDE fails) - Solution: Add
pathstotsconfig.jsoncompilerOptions - You might need to restart your IDE's TypeScript server (Cmd+Shift+P → "TypeScript: Restart TS Server")
If both fail, you have a more fundamental issue: components aren't installed where any configuration expects them. Use find to locate component files and verify against components.json.
When to Abandon Path Aliases Entirely
For some project setups - particularly monorepos, Yarn/pnpm workspaces, or projects with complex module resolution - path aliases create more problems than they solve. If you've spent more than an hour fighting with alias configuration, consider abandoning them.
Update components.json to use relative paths from your typical working directories:
{
"aliases": {
"components": "../components",
"utils": "../lib/utils"
}
}
This tells the Shadcn CLI to generate imports like import { Button } from "../components/ui/button". It works in any environment without configuration, though it assumes a specific directory structure.
The downside: if you later want absolute imports, you'll need to manually refactor all Shadcn component imports. But if absolute imports aren't working and you need to ship code, relative paths get you unstuck.
This is a pragmatic escape hatch, not a best practice. Use it when configuration debugging exceeds your available time budget.
Advanced Troubleshooting: Framework-Specific Issues
If the general solutions haven't worked, your issue might be specific to your framework or build tool. These advanced fixes address environment-specific quirks.
Next.js App Router vs Pages Router Path Resolution
Next.js 13+ introduced App Router, which changes default directory conventions. Pages Router expects code at the root (components/, lib/, pages/). App Router encourages grouping under app/ (app/components/, app/lib/).
Shadcn's CLI asks "Are you using React Server Components?" during init. If you answer "Yes" (App Router), it configures for the app/ directory structure. If you answer "No" (Pages Router or Vite), it uses root-level directories.
If you initialized for the wrong Router type, components might be in components/ui/ when App Router expects app/components/ui/. The fix: either move component files to match your Router, or reconfigure components.json and reinstall.
For App Router with components at root, verify your tsconfig.json includes:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./*"]
}
}
}
For App Router with components under app/, use:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./app/*"]
}
}
}
Adjust components.json aliases to match. The Shadcn CLI doesn't automatically detect your Next.js Router type, so this mismatch is common.
For more on Next.js configuration and debugging, see our Next.js troubleshooting guide.
Turbopack vs webpack Module Resolution Quirks
Next.js 13+ introduced Turbopack as an experimental bundler (enabled with next dev --turbo). Turbopack and webpack have slightly different module resolution behavior, though both read tsconfig.json paths.
If imports work with next dev but fail with next dev --turbo (or vice versa), you've hit bundler-specific behavior. Check Next.js GitHub issues for Turbopack path resolution bugs in your Next.js version.
Most Turbopack path issues were fixed in Next.js 14+, but if you're on an older version, disabling Turbopack (next dev without --turbo) sidesteps the problem. For production builds, Next.js uses webpack regardless, so Turbopack issues only affect local development.
If webpack builds work but Turbopack doesn't, you can override path resolution in next.config.js:
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
turbo: {
resolveAlias: {
'@': './',
},
},
},
};
module.exports = nextConfig;
This explicitly tells Turbopack how to resolve @/ imports, bypassing its automatic detection.
Monorepo Path Configuration (Turborepo, Nx)
Monorepos introduce additional complexity because tsconfig.json might be in a workspace package directory, but baseUrl and paths need to resolve relative to that package, not the monorepo root.
For Turborepo/pnpm workspaces with Shadcn in a package at apps/web/:
// apps/web/tsconfig.json
{
"compilerOptions": {
"baseUrl": ".", // Relative to apps/web/, not monorepo root
"paths": {
"@/*": ["./*"]
}
}
}
The components.json should also be in apps/web/components.json. Run npx shadcn-ui init from the apps/web/ directory, not from the monorepo root.
Each workspace package that uses Shadcn needs its own components.json and path configuration. Don't try to share components across workspace packages via path aliases - it breaks module resolution in subtle ways. If you need shared components, publish them as an internal package or use Turborepo's internal package linking.
For Nx monorepos, verify your tsconfig.base.json (at repo root) doesn't have conflicting path mappings that override the workspace package's settings. Nx's path mapping system can interfere with Shadcn's expectations.
React + Vite: Fixing '@' Alias in Non-TypeScript Projects
If you're using Vite with JavaScript (no TypeScript), you still need to configure the @ alias in vite.config.js, but you don't have tsconfig.json to maintain consistency.
// vite.config.js (JavaScript project)
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});
Note the extra setup for __dirname in ES modules - Vite uses ES modules by default, which don't have __dirname available.
For JavaScript projects, you also need jsconfig.json (not tsconfig.json) to get IDE autocomplete:
// jsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"exclude": ["node_modules", "dist"]
}
This gives VS Code the information it needs for import autocomplete and Cmd/Ctrl+Click navigation, even though you're not using TypeScript.
Shadcn with JavaScript (No TypeScript) Setup
Shadcn's CLI generates .tsx files by default, but you can use Shadcn components in JavaScript projects by manually converting the component files or configuring the CLI for .jsx.
During npx shadcn-ui init, answer "No" to "Would you like to use TypeScript?" The CLI will generate .jsx files instead and omit TypeScript-specific code.
You'll lose type safety for component props, but the components work identically. Make sure your bundler is configured to handle JSX (Vite and Create React App do this automatically).
For existing TypeScript-generated components, you can manually rename .tsx to .jsx and remove type annotations:
// Before (TypeScript)
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
// After (JavaScript - remove type imports)
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva } from "class-variance-authority"
This manual conversion is tedious for multiple components. If you're starting fresh, initialize with JavaScript mode. If you already have components installed as TypeScript, consider keeping them as .tsx - most bundlers handle .tsx files in JavaScript projects without issues.
Prevention: Setting Up Shadcn to Avoid Import Errors
Once you've fixed your current setup, follow these practices to prevent path resolution issues in future projects.
Best Practices for Initial Shadcn Configuration
Always run npx shadcn-ui init before adding any components. Don't skip to npx shadcn-ui add button assuming defaults will work. The initialization prompts customize configuration for your specific project structure.
Answer the prompts accurately. If you're building a Next.js App Router project, answer "Yes" to "Are you using React Server Components?" If you're using Vite with code in src/, choose @/ as your alias and verify it maps to ./src/* in your config files.
Review the generated components.json immediately after initialization. Verify the aliases section matches your project's conventions before installing components. It's easier to fix configuration before components exist than to reinstall everything after.
Document your path alias decision in your README or team wiki. If you chose @/components mapping to ./src/components, future developers need to know this convention when adding components or debugging imports.
Choosing Between @/components and ~/components
The @/ alias is the most common convention and works well with most tooling. TypeScript, Next.js, Vite, and webpack all handle @/ without issues. Stick with this unless you have a conflicting @/ mapping already.
The ~/ alias is an alternative that some developers prefer for aesthetics or to avoid conflicts with package scoping (npm packages like @company/package). It works identically to @/ but requires the same configuration in all three places (components.json, tsconfig.json, bundler).
Avoid using both @/ and ~/ in the same project. Pick one alias convention and use it consistently for all absolute imports, not just Shadcn.
Some projects use @@/ or #/ for internal packages in monorepos. If you're in that situation, choose a Shadcn alias that doesn't conflict with existing patterns.
The practical advice: use @/ unless you have a specific reason not to. It's the path of least resistance.
Testing Your Setup with a Simple Component First
After initialization, immediately test with a minimal component before installing your full UI kit:
# Test with button (simplest component)
npx shadcn-ui@latest add button
# Create a test file
# For Next.js: app/test/page.tsx
# For Vite: src/test.tsx
Import and render the button:
import { Button } from "@/components/ui/button";
export default function Test() {
return <Button>Import works!</Button>;
}
Run npm run dev and verify the button renders. If you see errors at this stage, fix them before installing more components. Debugging one component is easier than debugging twenty.
Once the test component works, install the rest of your components. If something breaks, you know the issue is with that specific component, not your baseline configuration.
Creating a components.json Template for Team Projects
If you're setting up Shadcn across multiple projects or with a team, create a canonical components.json template:
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "app/globals.css",
"baseColor": "slate",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}
Save this as shadcn-template.json in your organization's project scaffolding or documentation. When starting a new project, copy this file to components.json instead of running init interactively.
Pair it with corresponding tsconfig.json path configuration and Vite/webpack alias setup as code snippets in your documentation. This ensures consistency across projects and reduces "it works on my machine" issues.
For Next.js projects, create separate templates for App Router vs Pages Router since they have different directory conventions.
Pro Tip: Test your Shadcn setup with a simple component like button before installing complex components. If the basic import works, your configuration is solid.
When to Escalate: Beyond DIY Debugging
You've checked components.json, aligned tsconfig.json, verified bundler configuration, and reinstalled components. Imports still fail. This is the moment to recognize you've hit the 20% that needs expert intervention.
Signs Your Build Configuration Needs Expert Review
If you've spent more than an hour troubleshooting path aliases without progress, you're past the point of diminishing returns. Your time is better invested in getting expert help than continuing to guess at solutions.
The issue keeps returning after fixes. You get imports working, then adding a new component breaks everything again. This pattern indicates a deeper problem with how your build tools interact - something environmental rather than a simple configuration typo.
Module resolution works in development but fails in production builds, or vice versa. This suggests bundler-specific issues, potentially related to tree-shaking, code splitting, or optimization passes that affect path resolution differently than dev mode.
Error messages don't match anything in documentation or Shadcn UI GitHub issues. You're hitting an edge case that the community hasn't documented, which means trial-and-error debugging will be inefficient.
You're on a deadline and the broken imports are blocking deployment. Even if you could eventually solve it yourself, the opportunity cost of continued troubleshooting exceeds the cost of expert help. This is where the 80/20 rule flips.
How FlowQL Diagnoses Config-Layer Failures
FlowQL specializes in exactly these configuration-layer issues where AI tools can't help. Our senior developers have debugged hundreds of Shadcn path resolution failures across Next.js, Vite, monorepos, and custom webpack setups.
We start with live screen sharing to see your exact environment - error messages, file structure, and configuration files. We're not reading generic troubleshooting steps back to you. We're looking at your specific setup to identify the mismatch.
Common patterns we see: WSL2 file system case sensitivity causing issues that don't reproduce on Windows, pnpm hoisting behavior interfering with module resolution, conflicting Babel or SWC configurations overriding TypeScript paths, or stale build caches that persist through normal cache clearing.
We test hypotheses systematically. If the issue is bundler-specific, we'll try the same import with both webpack and Turbopack to isolate behavior. If it's TypeScript path mapping, we'll verify with tsc --showConfig and compare against what the bundler sees.
Most Shadcn configuration issues we diagnose resolve in under 30 minutes once we see your actual environment. The difference from DIY debugging: we know which diagnostic steps matter and can recognize configuration patterns that don't work even though they look correct.
When copy-paste components hit the invisible configuration layer, you need humans who understand module resolution, TypeScript paths, and bundler quirks. That's what FlowQL provides.
Stuck on component imports that should work? Book a live debugging session with FlowQL. We'll diagnose your exact path alias configuration issue and get Shadcn working in your environment in under 30 minutes, or you don't pay.
Reporting Path Resolution Bugs to Shadcn Maintainers
If you've definitively identified a bug in Shadcn's CLI rather than a project configuration issue, reporting it helps the community. The Shadcn maintainers are responsive to well-documented reports.
Before filing an issue, search Shadcn UI GitHub issues to see if someone has already reported the same behavior. If you find a matching issue, add your environment details as a comment rather than creating a duplicate.
When creating a new issue, provide:
- Exact Shadcn CLI version (
npx shadcn-ui@latest --version) - Framework and version (Next.js 14.1, Vite 5.0, etc.)
- Your
components.jsonfile (sanitized to remove project-specific paths) - Relevant parts of
tsconfig.jsonand bundler config - Exact steps to reproduce from a fresh project
- Error messages verbatim (screenshots or terminal output)
A minimal reproduction repository is ideal. Create a fresh Next.js or Vite project, run shadcn-ui init with specific answers, add a component, and demonstrate the import failure. Push to GitHub and link it in your issue.
Be specific about what you expected vs what happened. "Component not found after installation" is vague. "Running shadcn-ui add button with Next.js App Router and @/components alias creates files at components/ui/button.tsx but generates imports like @/app/components/ui/button which don't resolve" is actionable.
The maintainers often respond within a few days for legitimate bugs. They might ask for additional details or suggest configuration changes you haven't tried. Even if they don't immediately fix the bug, your report creates documentation for others hitting the same issue.
Conclusion
Shadcn's "component not found" errors always trace to one of six root causes: path alias mismatch between components.json and tsconfig.json, components installed to the wrong directory, missing or misconfigured TypeScript path mappings, case sensitivity issues across operating systems, bundler-specific module resolution differences, or incomplete Shadcn initialization.
Start with the 3-minute diagnostic: verify components.json exists, check component files are where expected, ensure TypeScript paths match aliases, test IDE resolution, and run a build. This pinpoints which configuration layer is broken.
For path alias issues, align all three pieces - components.json, tsconfig.json, and bundler config (Vite/webpack) - to use identical conventions. The @/ alias mapping to your project root or src/ directory is the most reliable pattern. If components were installed to the wrong location, reinitialize with npx shadcn-ui@latest init and reinstall components to the correct directory.
When configuration debugging fails, manual imports using relative paths work universally without configuration, though they sacrifice convenience. Use them as a temporary escape hatch while you investigate deeper issues.
Framework-specific problems - Next.js App Router vs Pages Router, Turbopack vs webpack, monorepo path resolution - require targeted fixes based on your specific environment. Most issues resolve by matching your project structure to what the Shadcn CLI expects during initialization.
But recognize when you've hit the 20% that needs human expertise. If you've spent an hour troubleshooting without progress, if the issue keeps returning, or if development vs production builds behave differently, that's the signal to escalate. FlowQL's live debugging sessions with senior developers typically resolve Shadcn path resolution issues in under 30 minutes by diagnosing the exact configuration mismatch in your environment.
The copy-paste promise of Shadcn is real for UI components, but the configuration layer underneath - the module resolution, path mapping, and bundler setup - is where AI tools can't help. When components install but don't import, you need expertise in the invisible layer that connects components.json to TypeScript to bundler. For more on systematic debugging workflow when configuration issues exceed quick fixes, check out our professional debugging guide.
Start with the quick diagnostic above, apply the targeted fix for your specific issue, and remember that needing expert help isn't failure - it's efficient problem-solving when you're past the point of productive trial-and-error.
Frequently Asked Questions
Why is Shadcn component not found after installation?
The CLI successfully installed component files, but your TypeScript configuration or bundler doesn't know how to resolve the @/ path alias. Verify that components.json aliases match tsconfig.json paths and that your bundler (Vite/webpack) is configured with the same alias mapping.
How do I fix Shadcn module not found error?
Check three things: (1) Component files exist at the location in components.json, (2) tsconfig.json has baseUrl and paths configured to match the alias, (3) Your bundler (Vite needs vite.config.ts alias, Next.js reads from tsconfig automatically) knows how to resolve the path.
What is the correct components.json configuration for Shadcn?
The standard configuration uses "components": "@/components" and "utils": "@/lib/utils" aliases. This must match your tsconfig.json paths - if you use @/* mapped to ./src/*, components install to src/components/ui/. Review the Shadcn UI official installation guide for framework-specific setup.
How do I verify Shadcn components are installed correctly?
Run find . -name "*.tsx" -path "*/ui/*" to locate component files, then verify the path matches what components.json expects. Test import resolution by creating a file that imports a component - if TypeScript shows no errors and npm run build succeeds, installation is correct.
Why does Shadcn import work in some files but not others?
This usually indicates relative path issues or inconsistent alias usage. Verify all imports use the same pattern (@/components/ui/button consistently, not mixing with ~/components or relative paths). Case sensitivity differences between development (macOS/Windows) and production (Linux) can also cause this.
How do I fix TypeScript path aliases in Shadcn?
Add to tsconfig.json: "baseUrl": "." and "paths": { "@/*": ["./*"] } (or "./src/*" if using src directory). The path mapping must match what components.json uses for the components alias. Restart your IDE's TypeScript server after changing tsconfig. See TypeScript path mapping documentation for detailed configuration.
Can I use Shadcn without the CLI installation?
Yes, you can manually copy component code from Shadcn components documentation and paste into your project. You'll need to manually install dependencies (Radix UI primitives documentation for the underlying components) and configure Tailwind CSS configuration docs. The CLI automates this setup but isn't strictly required.
For related build configuration debugging when Shadcn issues appear during deployment, see our build configuration debugging guide. And if you're dealing with these configuration layers alongside other development challenges, our guide on AI tool limitations covers when to escalate beyond DIY troubleshooting.
Subscribe to our blog
Get the latest guides and insights delivered to your inbox.
Join the FlowQL waitlist
Get early access to our AI search optimization platform.
Related Articles
Fix: params are undefined in Next.js App Router Server Component
params undefined in your Next.js server component? Learn how to handle the async breaking change in Next.js 15 and properly await your dynamic route data.
Fix: Shadcn Select Dropdown Hidden or Behind Other Elements
Shadcn Select dropdown not showing up? Learn how to fix z-index conflicts and properly use Portals to break through CSS stacking contexts in Tailwind.
Fix: Tailwind CSS Classes Not Applying in Shadcn UI (2025)
Shadcn styling not working? Learn how to fix Tailwind CSS classes not applying to Shadcn components by auditing your tailwind.config.js content paths and cn() utility.