言語を切り替える
テーマを切り替える

shadcn/ui トラブルシューティング:スタイル競合・コンポーネント非表示・型エラー

画面のボタンコンポーネントをじっと見つめる——本来はきれいな青いボタンのはずなのに、今は丸角すらない、ただの HTML button にしか見えません。

この 3 か月、書いたコードより踏んだ落とし穴の方が多かったかもしれません。スタイル競合、コンポーネントの表示失敗、TypeScript エラー——こうした問題は shadcn/ui の「名物」のようなもので、新しいプロジェクトのたびにいくつか出会います。

今回は、そうしたよくある問題と解決策をまとめました。少しでも遠回りを減らせれば幸いです。


スタイル競合の切り分け

スタイル競合は最も多い問題で、全体のおよそ 4 割を占めます。主な原因はいくつかあります。

CSS 変数の競合

shadcn/ui はテーマカラーの管理に CSS 変数を使い、これらは globals.css に定義されています。

:root {
  --background: 0 0% 100%;
  --foreground: 222.2 84% 4.9%;
  --primary: 222.2 47.4% 11.2%;
  --primary-foreground: 210 40% 2%;
}

ここで問題が起こります。プロジェクトにすでに独自のテーマ設定がある場合や、shadcn/ui をインストールする前に Tailwind のカラー設定を変えていた場合、この 2 つの設定がぶつかることがあります。

どう切り分けるか?

まず globals.css を開き、CSS 変数がすべて揃っているか確認します。次に tailwind.config.js の colors 設定をチェックします。

module.exports = {
  theme: {
    extend: {
      colors: {
        border: "hsl(var(--border))",
        input: "hsl(var(--input))",
        ring: "hsl(var(--ring))",
        background: "hsl(var(--background))",
        foreground: "hsl(var(--foreground))",
        primary: {
          DEFAULT: "hsl(var(--primary))",
          foreground: "hsl(var(--primary-foreground))",
        },
      },
    },
  },
}

この両者は対応している必要があります。どれか変数が欠けると、対応するスタイルが効きません。

経験から:shadcn/ui をインストールする前に、tailwind.config.jsglobals.css をバックアップしておきましょう。インストール後に 2 つのファイルの差分を比較し、上書きされた設定を手動で戻します。

Shadow DOM と Tailwind の競合

これはなかなか面白い問題です。Shadow DOM は本来スタイルを分離するための仕組みですが、Tailwind のクラス名は Shadow DOM の境界を越えられません。

典型的なのが Dialog コンポーネントです。DialogContent は Portal で document.body に描画されるため、Shadow DOM の範囲外に出てしまい——スタイルがすべて消えます。

解決策は 2 つあります

1 つ目は、いっそ Shadow DOM を使わない方法です。

const MyDialogWC = r2wc(MyDialog, {
  shadow: null  // Shadow DOM を無効化
});

こうすれば Portal は正常に動きますが、スタイルの分離は失われます。グローバルスタイルを手動で管理し、クラス名の衝突にも気を付ける必要があります。

2 つ目は、Safelist でクラス名を強制的に含める方法です。

// tailwind.config.js
module.exports = {
  safelist: [
    'bg-primary',
    'text-primary-foreground',
    'hover:bg-primary/90',
    'bg-red-500',
    'h-9',
    'h-10',
    'px-3',
    'px-4',
  ],
}

この方法ならスタイルの生成を保証できますが、Safelist は CSS ファイルのサイズを増やします。トレードオフを考える必要があります。

他の UI ライブラリとの共存

プロジェクトですでに MUI(Material-UI)を使っていて、shadcn/ui へ移行したい場合、スタイル競合に出会います。

根本原因は Tailwind の Preflight です。これはブラウザのデフォルトスタイルをすべてリセットします。MUI のスタイルもリセットされ、コンポーネントの表示がおかしくなります。

よくある試み

Preflight を無効化する人もいます。

module.exports = {
  corePlugins: {
    preflight: false,  // Preflight を無効化
  },
}

ただしこれには副作用があります。Tailwind 自体のスタイルも影響を受け、一部のコンポーネントが正しく表示されなくなることがあります。

より良い方法

Tailwind の prefix 機能を使い、すべての Tailwind クラスに接頭辞を付けます。

module.exports = {
  prefix: 'tw-',  // すべてのクラス名が tw-bg-blue-500 になる
}

こうすれば Tailwind のクラスと MUI のクラスはぶつかりません。ただし、各クラス名の前に手動で tw- を付ける必要があり、少し面倒です。

おすすめ:すでに MUI コンポーネントが多いプロジェクトなら、慌てて全部移行しないことです。まず prefix 方式で共存させ、新しいコンポーネントは shadcn/ui、古いものは少しずつ書き換えていきます。

Tailwind の設定が上書きされる

これは何度も落とし穴にはまりました。

npx shadcn-ui@latest init を実行すると、Tailwind の設定ファイルが上書きされます。特に plugins 配列——以前 @tailwindcss/forms などのプラグインを設定していた場合、それらが消えてしまいます。

症状ははっきりしています。フォーム入力欄のスタイルが急におかしくなったり、一部のコンポーネントのスタイルがまったく当たらなくなったりします。

切り分けの手順

  1. インストール前の tailwind.config.js のバックアップを開く
  2. インストール後の設定ファイルと比較する
  3. 消えたプラグインを戻す
module.exports = {
  // ... その他の設定
  plugins: [
    require("@tailwindcss/forms"),  // 戻す
    require("tailwindcss-animate"),
  ],
}

予防策:shadcn/ui をインストールする前に設定ファイルをバックアップします。あるいは専用の設定管理スクリプトを用意し、すべてのプラグインを記録しておきます。


コンポーネントが表示されない問題の切り分け

スタイルは問題ないのに、コンポーネントがまったく表示されない?こうしたケースもよくあります。

content パスの設定ミス

Tailwind は、どのファイルで自分のクラス名が使われているかを知る必要があります。そうして初めて対応する CSS を生成できます。この設定が tailwind.config.jscontent フィールドです。

問題はたいてい、shadcn/ui のコンポーネントディレクトリが含まれていないことです。

設定を確認

module.exports = {
  content: [
    './src/app/**/*.{ts,tsx}',
    './src/components/**/*.{ts,tsx}',  // これは必須
    './app/**/*.{ts,tsx}',
    './pages/**/*.{ts,tsx}',
  ],
}

コンポーネントを node_modules 内のある UI ライブラリに置いている場合は、次も追加します。

content: [
  // ... その他のパス
  './node_modules/@your-ui-lib/**/*.{ts,tsx}',
]

経験から:新しいコンポーネントディレクトリを作るたびに、そのパスを content 設定に追加するのを忘れないことです。そうしないと Tailwind がファイルをスキャンできず、クラス名が生成されません。

globals.css のパス問題

shadcn/ui はテーマ変数を定義するための CSS ファイルを必要とします。このファイルのパスは components.json で設定します。

問題はたいてい、パスを書き間違えているか、globals.css が複数あることです。

切り分け方法

まず components.json を確認します。

{
  "style": "default",
  "css": "src/app/globals.css",  // このパス
}

そのうえで次を確認します。

  1. このファイルは本当に存在するか?
  2. プロジェクトに globals.css は 1 つだけか?
  3. globals.css はメインファイルに正しく import されているか?

globals.css が複数ある場合は、余分なものを削除し、1 つだけ残します。

import の確認

Next.js プロジェクトでは、globals.css は app/layout.tsxpages/_app.tsx で import する必要があります。

import '@/app/globals.css'  // または './globals.css'

import されていないと、CSS 変数が効かず、コンポーネントのスタイルがすべて消えます。

CSS 変数が未定義

globals.css ファイルは存在するのに、変数が定義されていないこともあります。

典型的なのがダークモードです。dark mode に切り替えるとコンポーネントの色がおかしい——これはダークモード用の CSS 変数が設定されていないからかもしれません。

globals.css を確認

:root {
  --background: 0 0% 100%;
  --foreground: 222.2 84% 4.9%;
}

.dark {
  --background: 222.2 84% 4.9%;
  --foreground: 210 40% 2%;
}

.dark クラス内の変数は必ず定義します。そうしないとダークモードのコンポーネントにスタイルが当たりません。

Tailwind v4 の特殊なケース

Tailwind v4 を使っている場合、設定方法が異なります。

@theme inline {
  --color-background: var(--background);
  --color-foreground: var(--foreground);
  --color-primary: var(--primary);
}

この @theme inline のマッピングは必須です。そうしないと Tailwind v4 はこれらの変数を認識しません。

import パスの大文字小文字の問題

この落とし穴は 2 回踏みました。

Windows ではファイル名の大文字小文字を区別しませんが、Linux/Mac は区別します。ローカル開発では問題ないのに、本番環境にデプロイするとエラーになります。

症状はたいてい、コンポーネントがローカルでは描画されるのに、サーバーへ上げるとモジュールが見つからない、というものです。

典型的なミス

// ❌ 誤り:Button の B が大文字
import { Button } from "@/components/ui/Button"

// ✅ 正しい:button の b は小文字
import { Button } from "@/components/ui/button"

shadcn/ui のコンポーネントファイル名はすべて小文字です。import するときは小文字のパスを使う必要があります。

切り分け方法

すべてのコンポーネントの import 文を確認し、パスが実際のファイル名と一致しているかチェックします。特に本番環境のエラーメッセージに注目します。


TypeScript 型エラーの切り分け

TypeScript のエラーは比較的少ないですが、出会うとやはり厄介です。

variant 属性の型エラー

shadcn/ui の Button コンポーネントには variant 属性があり、ボタンのスタイル(default、destructive、outline など)を切り替えます。

エラーメッセージはたいてい次のようになります。

Type '{ variant: string }' is not assignable to type 'IntrinsicAttributes & ButtonProps'.
Property 'variant' does not exist on type 'IntrinsicAttributes & ButtonProps'.

根本原因

Button コンポーネントの型定義で、variant 属性が正しくエクスポートされていません。

切り分け方法

components/ui/button.tsx を開き、variant の型定義を確認します。

const buttonVariants = cva(
  "inline-flex items-center justify-center rounded-md text-sm font-medium",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground hover:bg-primary/90",
        destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
        outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
      },
    },
  }
)

interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  // ここの VariantProps は必須
}

VariantProps<typeof buttonVariants> が欠けると、variant 属性の型が失われます。

経験から:このエラーに出会ったら、まずコンポーネントの型定義を確認します。VariantProps が正しく継承されているかをチェックします。

React のバージョン非互換

React 19 を使っていて、依存の一部がまだ React 19 に対応していない場合、型エラーに出会います。

エラーメッセージはたいてい次のようになります。

npm error ERESOLVE unable to resolve dependency tree
npm error Found: react@19.0.0-rc-69d4b800-20241021

解決策は 2 つあります

1 つ目は、強制インストールです。

npm install --legacy-peer-deps
# または
npm install --force

これは peer dependency のバージョン要件を無視します。ただし互換性の問題が出る可能性があります。

2 つ目は、React のダウングレードです。

npm install react@18 react-dom@18

React 18 を使い、依存が更新されてから上げます。

おすすめ:新規プロジェクトは React 18 の方が安定します。shadcn/ui やほかの依存がすべて React 19 に対応したら、アップグレードを検討しましょう。

React Hook Form の型問題

shadcn/ui の Form コンポーネントを React Hook Form や Zod と組み合わせて使うと、型のマッピング問題に出会います。

エラーメッセージはたいてい次のようになります。

Type 'info.${number}.fileName' is not assignable to type '"info" | "info.0" | "info.0.fileName"'

これは動的なフォームフィールドの型問題です。Zod Schema の型と FormField の name 属性の型が一致していません。

解決策

Schema の型が正しく推論されるようにします。

const formSchema = z.object({
  email: z.string().email(),
  password: z.string(),
})

type FormValues = z.infer<typeof formSchema>  // この型推論は必須

const form = useForm<FormValues>({
  resolver: zodResolver(formSchema),
})

FormField の name 属性は Schema のフィールド名に自動でマッチします。

経験から:動的フォーム(例えば useFieldArray を使うもの)は型がより複雑です。Zod Schema の定義と TypeScript の型推論を丁寧に確認する必要があります。

型依存の不足

TypeScript のエラーが @types/react@types/react-dom 未インストールのせいで出ることもあります。

エラーメッセージは次のようになることがあります。

Could not find a declaration file for module 'react'

解決策

npm install -D @types/react @types/react-dom

インストール後、TypeScript サーバーを再起動します(VS Code では Ctrl+Shift+P から “TypeScript: Restart TS Server” を入力)。

予防策:新規プロジェクトでは最初から型依存をインストールします。エラーが出てから気付くのは避けましょう。


ベストプラクティスと予防策

これだけ落とし穴を踏んできて、いくつかの予防策をまとめました。

設定管理のルール

設定ファイルのバックアップ

shadcn/ui をインストールしたり Tailwind の設定を変更したりする前に、バックアップします。

cp tailwind.config.js tailwind.config.js.backup
cp globals.css globals.css.backup

インストール後に差分を比較し、設定を手動でマージします。

設定ファイルは 1 つに

1 つのプロジェクトで tailwind.config.jsglobals.css も 1 つだけにします。複数の設定ファイルを作らないこと。競合しやすくなります。

content パスを網羅する

content 設定はすべてのコンポーネントディレクトリを含めます。

content: [
  './src/**/*.{ts,tsx}',        // ワイルドカードで全ディレクトリをカバー
  './app/**/*.{ts,tsx}',
  './pages/**/*.{ts,tsx}',
  './components/**/*.{ts,tsx}',
]

依存バージョンの管理

peerDependencies の確認

新しい依存をインストールする前に、その peerDependencies を確認します。

npm info <package> peerDependencies

依存が React 18 を要求しているのにプロジェクトが React 19 を使っているなら、互換性を考える必要があります。

型依存を定期的に更新

npm update @types/react @types/react-dom

型宣言と React のバージョンを同期させておきます。

テスト戦略

インストール後すぐにテスト

shadcn/ui のインストールが終わったら、すぐにスタイルをテストします。

  1. シンプルなページを作り、いくつか shadcn/ui コンポーネントを使う
  2. スタイルが正しく表示されるか確認する
  3. ダークモードの切り替えをテストする
  4. TypeScript のコンパイルチェックを実行する

本番環境でのテスト

ローカル開発で問題なくても、本番環境で問題ないとは限りません。

npm run build
npm run preview

ビルド後にプレビューし、スタイルと型が正常かを確認します。


まとめ

いろいろ書きましたが、shadcn/ui のよくある問題は結局この 3 か所に集中します。

  1. スタイル競合:設定ファイルの上書き、CSS 変数の競合、他の UI ライブラリとの共存問題
  2. コンポーネントが表示されない:content パスの設定ミス、globals.css のパス問題、import パスの大文字小文字
  3. TypeScript 型エラー:variant 属性の型の欠落、React のバージョン非互換、型依存の不足

問題に出会ったら、次の順番で切り分けます。

  1. まず設定ファイルを確認する(tailwind.config.js、globals.css)
  2. 次にパス設定を確認する(content、import パス)
  3. 最後に型定義を確認する(コンポーネントの型、依存のバージョン)

shadcn/ui を使い始めたばかりなら、まずは空のプロジェクトで一連の流れを通しで試すのがおすすめです。設定とよくある問題に慣れてから、実プロジェクトで使いましょう。

shadcn/ui はとても便利ですが、設定は確かに少し複雑です。これらの切り分け方法を身につけておけば、問題に出会っても慌てずに済みます。

FAQ

shadcn/ui をインストールしたらスタイルが全部消えたのはなぜ?
最も多い原因は Tailwind の設定が上書きされたことです。次を確認します。

• tailwind.config.js の plugins 配列が欠けていないか
• globals.css のパスが正しいか
• CSS 変数がすべて定義されているか

解決策:インストール前に設定ファイルをバックアップし、インストール後に差分を比較して、消えた設定を手動で戻します。
ダークモードに切り替えるとコンポーネントのスタイルが崩れるのはなぜ?
globals.css の .dark クラス内の CSS 変数が完全に定義されているか確認します。

• .dark クラスが存在すること
• すべてのテーマ変数を再定義すること
• Tailwind v4 では @theme inline で変数をマッピングする必要があること
shadcn/ui は MUI と共存できる?
共存できますが、Tailwind の prefix を設定する必要があります。

• prefix: 'tw-' を設定してすべての Tailwind クラスに接頭辞を付ける
• 新しいコンポーネントは shadcn/ui、既存は MUI のまま残す
• 一度に全部変えず、段階的に移行する

Preflight の無効化は Tailwind 自体のスタイルに影響するのでおすすめしません。
Button の variant 属性が TypeScript エラーになるときは?
Button コンポーネントの型定義を確認します。

• VariantProps<typeof buttonVariants> が継承されていること
• コンポーネントファイルが型を完全にエクスポートしていること
• @types/react と @types/react-dom がインストールされていること

型定義が欠けている場合は、npx shadcn@latest add button を再実行してコンポーネントを入れ直します。
React 19 で shadcn/ui は使える?
使えますが、互換性の対応が必要です。

• インストール時に --legacy-peer-deps か --force を使う
• または package.json の overrides で react-is のバージョンを指定する
• 新規プロジェクトはまず React 18 で始め、依存が更新されてから上げるのがおすすめ
ローカルでは動くのに本番でモジュールが見つからないエラーになるのは?
たいてい import パスの大文字小文字の問題です。

• shadcn/ui のコンポーネントファイル名はすべて小文字(button.tsx)
• import パスはファイル名と一致させる(@/components/ui/button)
• Windows は大文字小文字を区別しないが、Linux/Mac は区別する。ローカルでは問題なくても本番でエラーになる

すべての import 文を確認し、パスが完全に一致しているかチェックします。

5分で読めます · 公開日: 2026年4月2日 · 更新日: 2026年6月8日

シリーズの読書導線 第 11 / 11 記事

Tailwind と shadcn/ui 実践ガイド

検索からこのページに来た場合は、前後の記事もあわせて読むと同じテーマの理解がかなり早く深まります。

シリーズ全体を見る

関連記事

コメント

GitHubアカウントでログインしてコメントできます