shadcn-ui/ui · error · Error

The cn migration requires Tailwind CSS v4 and tailwind-merge

Error message

The cn migration requires Tailwind CSS v4 and tailwind-merge v3. Tailwind CSS v3 projects should continue using tailwind-merge v2.

What it means

The cn migration rewrites cn() helpers built on tailwind-merge v3 semantics, which only work with Tailwind CSS v4. assertTailwindCompatibility reads tailwindcss and tailwind-merge versions from package.json (dependencies/devDependencies/optional/peer) and throws if the declared major of either is below the required minimum (v4 and v3 respectively; workspace/protocol specifiers are treated as satisfying).

Source

Thrown at packages/shadcn/src/migrations/cn/index.ts:144

  cwd: string,
  migratedPackages: Set<OldPackage>
) {
  if (!migratedPackages.has("tailwind-merge")) {
    return
  }

  const packageInfo = getPackageInfo(cwd, false)
  const tailwindVersion = findDependencyVersion(packageInfo, "tailwindcss")
  const tailwindMergeVersion = findDependencyVersion(
    packageInfo,
    "tailwind-merge"
  )

  if (
    (getDeclaredMajor(tailwindVersion) ?? 4) < 4 ||
    (getDeclaredMajor(tailwindMergeVersion) ?? 3) < 3
  ) {
    throw new Error(
      "The cn migration requires Tailwind CSS v4 and tailwind-merge v3. Tailwind CSS v3 projects should continue using tailwind-merge v2."
    )
  }
}

function findDependencyVersion(
  packageInfo: ReturnType<typeof getPackageInfo>,
  packageName: string
) {
  return (
    packageInfo?.dependencies?.[packageName] ??
    packageInfo?.devDependencies?.[packageName] ??
    packageInfo?.optionalDependencies?.[packageName] ??
    packageInfo?.peerDependencies?.[packageName]
  )
}

function getDeclaredMajor(version?: string) {

View on GitHub (pinned to 5c7072da67)

Solutions

  1. Upgrade to Tailwind CSS v4 (`npx @tailwindcss/upgrade`) before running the cn migration.
  2. Upgrade tailwind-merge to v3: `npm install tailwind-merge@^3`.
  3. If you must stay on Tailwind v3, keep tailwind-merge v2 and do not run the cn migration for that project.
  4. Check package.json for stale pins/resolutions (e.g. overrides forcing tailwind-merge@2) and remove them.

Example fix

// before (package.json)
"tailwindcss": "^3.4.0",
"tailwind-merge": "^2.2.0"
// after
"tailwindcss": "^4.0.0",
"tailwind-merge": "^3.0.0"
Defensive patterns

Strategy: validation

Validate before calling

import { readFileSync } from 'fs'
const pkg = JSON.parse(readFileSync('package.json', 'utf8'))
const deps = { ...pkg.dependencies, ...pkg.devDependencies }
if (parseInt(deps['tailwindcss']?.match(/\d+/)?.[0] ?? '4', 10) < 4 ||
    parseInt(deps['tailwind-merge']?.match(/\d+/)?.[0] ?? '3', 10) < 3) {
  throw new Error('Upgrade to tailwindcss@4 and tailwind-merge@3 before migrating')
}

Prevention

When it happens

Trigger: migrateCn detecting tailwind-merge usage in migrated files while package.json declares tailwindcss 3.x or tailwind-merge 2.x (or neither is declared at a valid semver major).

Common situations: Upgrading a Tailwind v3 project to the new cn utility without first migrating Tailwind itself; a monorepo app that still pins tailwind-merge@^2 while others use v3; the packages missing from package.json entirely so majors fall back to 4/3 — the throw only fires on a declared lower major.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of shadcn-ui/ui@5c7072da67 (2026-09-07). Data as JSON: /api/errors/befb52891d984e33. Report an issue: GitHub.