こんにちは、フロントエンドエンジニアとして働いているぱやぴです。
これまで主にMantine UIを使って実務を進めてきましたが、最近注目されている「shadcn/ui」を試してみました。
本記事では、shadcn/uiの概要やメリット・デメリット、実際に触ってみた感想などを紹介していきます。Mantine UIに慣れている方はもちろん、その他のコンポーネントライブラリを使っている方にも参考になれば幸いです。
shadcn/uiとは?
shadcn/uiは、React向けのコンポーネントコレクションであり、UI構築をよりシンプルかつ柔軟にすることを目的としています。主な特徴としては、次の点が挙げられます。
- Radix UIのコンポーネントをベースにしている
- スタイリングはTailwind CSSを利用
- セマンティクスやアクセシビリティ(a11y)を考慮したデザイン
- 公式サイトからコンポーネントをコピー&ペーストで導入可能
- 必要なものだけ導入しやすい疎結合な設計
ビルド済みのコンポーネントを導入するのではなく、プロジェクト内にソースコードとして取り込む形式が採用されています。
後述するメリットにも大きく関係しますが、この方針によって細かいカスタマイズやアップデートに柔軟に対応しやすくなっています。
メリット
1. Radix UI由来のUIパターン & アクセシビリティ
shadcn/uiはRadix UIをベースとしており、アクセシビリティ(a11y)を重視したUIコンポーネントが揃っています。以下の点がメリットです。
- UIパターン
- Tailwind CSSと統合されており、UIデザインを簡単に適用可能
- モダンなUIパターンを利用しつつ、ゼロからのデザイン構築の手間を軽減
- アクセシビリティ
- WAI-ARIAのベストプラクティスが適用されているため、デザインを変更してもa11yが担保される
- キーボード操作やスクリーンリーダーへの対応が優れている
- 必要に応じてデザインシステムやスタイルを柔軟に追加可能
2. Tailwind CSSとの親和性 & 高いカスタマイズ性
shadcn/uiはTailwind CSSでスタイリングされているため、次のような利点があります。
- 既存のTailwindベースのプロジェクトに組み込みやすい
- クラス名を直接編集することで、デザインやレイアウトを自由に変更できる
また、コンポーネントをnpmパッケージとして利用するのではなく、プロジェクト内に直接ソースコードを取り込むスタイルのため、必要に応じて自由に修正できる柔軟さがあります。
結果的に、プロダクト独自のデザイン要件や将来的な拡張にも対応しやすい点が非常に魅力的です。
3. CLIやコピー&ペーストで導入が容易
shadcn/uiにはCLIツールが用意されており、以下のようなメリットがあります。
- コマンド一つでコンポーネントをローカルに自動生成
- コンポーネント名を指定するだけで簡単に追加・削除が可能
また、公式サイトからコピー&ペーストする方法でも導入できるため、初めて触れる場合でもセットアップがわかりやすく、すぐに実装に移れるのがメリットです。
4. v0との連携
v0で生成されたコードは shadcn/ui のコンポーネントを使用しているため、そのままプロジェクトに組み込むことができ、プロトタイピングの速度が大幅に向上します。
また、v0が生成するUIはshadcn/uiに基づいているため、AIを用いながらデザインの一貫性を保つことができます。
以下v0について紹介されていた記事になります。
5. オープンソースかつコミュニティ駆動で進化し続ける
shadcn/uiはオープンソースとして公開され、コミュニティが積極的に開発をサポートしています。
- バグ修正や新機能要望への対応が比較的早い
- デザインやアクセシビリティ強化のプルリクエストが日々取り込まれている
そのため、今後も継続的なアップデートが見込まれ、時代に合わせたUI/UXの改善が期待できます。
デメリット
一方で、shadcn/uiを使うにあたってのデメリットや注意点としては、以下が挙げられます。
- コンポーネント更新の手間 npmパッケージとして管理されているライブラリと異なり、shadcn/uiはソースコードを直接取り込む形式です。 新しいバージョンがリリースされた場合は、自分たちで差分を確認しながら更新しなければならないため、保守コストが増える可能性があります。
- Tailwindベースであること Tailwind CSSでの開発が前提となるため、Tailwind未経験の場合は慣れが必要です。 既存プロジェクトに導入するには、ビルドチェーンの調整などが必要になる場合もあるでしょう。
- 実装責任がより増す コンポーネントをコピーしているとはいえ、プロジェクト内でコンポーネントとして管理することになります。 そのため、状況によっては同じコンポーネントなのにバージョン差異が生じるなどのリスクも考慮が必要です。
実際に動かしてみる
ここでは、簡単に手順を紹介します。(プロジェクト環境によっては多少異なる場合があります) 詳細な導入方法は公式ドキュメントを確認してください。
- shadcn/uiのCLIで導入
npx shadcn@latest init
- Tailwind CSSのセットアップ
npm install -D tailwindcss@latest autoprefixer@latest
- Next.js + Tailwind CSSのテンプレートを利用するか、既存プロジェクトであればTailwindの設定ファイル(
tailwind.config.js
)を用意します。 postcss.config.js
やtailwind.config.js
を適宜編集し、Tailwindを有効にします。
- コンポーネントのインストール
- shadcn/uiの公式サイトで目的のコンポーネントを選択し、CLIまたはコピー&ペーストでプロジェクトに取り込みます。
- 今回はサンプルとして
button
をCLIで取り込みます。
- 今回はサンプルとして
- shadcn/uiの公式サイトで目的のコンポーネントを選択し、CLIまたはコピー&ペーストでプロジェクトに取り込みます。
npx shadcn@latest add button
- 取り込んだコンポーネントは
components/ui
のようなディレクトリに配置されます。
- 動作確認
- ローカルで開発サーバを起動し、ブラウザ上でUIを確認します。
import { Button } from import { Button } from "./components/ui/button";
export default function App() {
return <Button>Click me</Button>;
}
- カスタマイズT
- Tailwindのクラスを使って自由にデザインを変更できます。
- 今回はxsサイズをxsサイズの追加とデフォルトでのボタンの表示を背景色に合わせるようにします。
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";
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
variants: {
variant: {
+ default: "bg-background hover:bg-accent hover:text-accent-foreground",
+ primary:
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline:
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
+ xs: "h-7 rounded-md px-2 text-xs",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
);
}
);
Button.displayName = "Button";
export { Button, buttonVariants };
- sizeに今回追加した
xs
を指定します
export default function App() {
return <Button size="xs">Click me</Button>;
}
- ボタンの色とサイズが変わりました。(わかりやすく変更前のものを並べて表示しています。)
実際に試してみた感想としては、コンポーネントをコピー&ペーストするだけで「動くUI」がすぐに生成される点が非常に快適でした。
Mantine UIほどドキュメントが整備されていない印象はありましたが、UIの見せ方や挙動の細かな調整がしやすいので、個人的にはプロダクトの独自性を出したいケースに向いていると感じます。
終わりに
今回は、新しく注目されているshadcn/uiについて、概要からメリット・デメリット、実際に動かしてみた感想をまとめました。
とても簡単に導入することができ、感動しました。
長らくMantine UIを愛用してきた身としては、UIライブラリを丸ごと導入する手軽さや手厚いドキュメントを維持しつつ、もう少し細かいカスタマイズが欲しい…と感じることもありました。
そんなときにshadcn/uiの「コンポーネントを自前で管理できる」という方針は、とても魅力的に思えました。
今後のプロジェクトでは、デザイナーや他のエンジニアがどれくらい自由度を求めるかによって、UIフレームワークを選ぶ際の選択肢が増えそうです。アクセシビリティとデザインを両立したい方や、コンポーネントをより細かく制御したいという方には、ぜひ試してみてはいかがでしょうか。
ここまで読んでいただきありがとうございました。