Your v0 component throws a Module-not-found error the second you paste it in — here's why

v0-generated code assumes shadcn/ui components already exist in your project, because shadcn isn't an npm package — it's source files copied in. Paste v0 output into a project that hasn't been through that setup and every @/components/ui import breaks. Here's the actual mechanism and the fix.

You copy a component out of v0, paste it into your existing Next.js project, and npm immediately throws: Module not found: Can't resolve '@/components/ui/button'. Or @/lib/utils. Sometimes both. The component looked complete in v0’s preview — it rendered, it worked — so it’s not obvious why the exact same code fails the second it’s in your own codebase.

Why this happens

shadcn/ui isn’t an npm package. There’s nothing to npm install — the whole model is that component source files get copied directly into your project (usually under components/ui/), so you own and can edit the code rather than importing it from a black box. v0 is built on that same model: when it generates a card, a form, or a dashboard, the JSX it shows you assumes @/components/ui/button, @/components/ui/card, and the cn() helper in @/lib/utils already exist as real files in your project, wired up through a path alias.

Inside v0’s own preview environment, they do — v0 provisions that scaffolding for you behind the scenes. The moment you copy-paste the code into a project that doesn’t have that scaffolding, the import points at files that were never created. The error isn’t a bug in what v0 generated; it’s that copy-paste only moved the code, not the components and config it depends on.

The @/ alias makes this worse to diagnose, because it looks like a typo rather than a missing file. If your project’s tsconfig.json (or jsconfig.json) doesn’t have a paths entry mapping @/* to your source root, you’ll get a resolution error even for files that do exist — a second, easy-to-conflate failure mode on top of the missing-component one.

How to tell if this is your problem

  1. Check whether the imported file actually exists. If components/ui/button.tsx isn’t in your project at all, it’s the missing-component case — v0 assumed shadcn/ui was already installed there.
  2. If the file does exist, check tsconfig.json for a paths alias. No "@/*": ["./*"] (or equivalent) entry means the alias itself isn’t configured, independent of whether the component exists.
  3. Check for a components.json at your project root. Its absence means shadcn/ui was never initialized in this project — the CLI that manages the alias, the cn() helper, and component installs hasn’t been run here yet.

The fix

Don’t copy-paste v0 output into an existing project by hand — use the shadcn CLI’s v0 integration instead. From your project root:

npx shadcn@latest add "https://v0.dev/chat/b/YOUR_BLOCK_ID"

The block URL is available from the ”…” menu on any v0 generation (“Add to Codebase” / “Open in shadcn”). This pulls the actual component source, drops it into components/ui/, and — critically — installs whatever it depends on, instead of leaving you to reverse-engineer the import list yourself.

If components.json doesn’t exist yet, initialize shadcn/ui first:

npx shadcn@latest init

This sets the @/* path alias in tsconfig.json, creates lib/utils.ts with cn(), and configures the Tailwind theme tokens v0’s generated components expect (border radius, color variables). Skipping this step is why a component can look right in v0 and still be visually broken once it does compile in your project — the CSS variables it references don’t exist yet either.

If you’re stuck manually reconciling an existing mismatch, run npx shadcn@latest add button (or whichever component is missing) directly — the CLI will detect your existing components.json and drop the file in the right place with the right dependencies, which is faster than tracking down each import by hand.

How to avoid this next time

Treat v0 output as something you install, not something you copy. If a project doesn’t have components.json yet, run shadcn init before you paste a single component in — every import v0 generates is written assuming that step already happened.

Comments