コンポーネント「ライブラリ」ではなく「コレクション」?shadcn/uiに入門してみた

こんにちは、サイオステクノロジーの遠藤です。

今回は、JavaScript Rising Starsで2023年、2024年と二年連続で1位に輝いたUIコンポーネントコレクションであるshadcn/uiについてまとめていきます。

では始めていきましょう!

shadcn/uiとは?

shadcn/uiはRadix UIとTailwindCSSを用いて作成されたUIコンポーネントコレクションです。

shadcn/uiの一番の特徴は公式ドキュメントにも This is not a component library. と書かれているように既存のコンポーネントライブラリではない点にあります。

一般的なコンポーネントライブラリの場合、NPMからパッケージをインストールしてコンポーネントをインポートすることで対象のコンポーネントを使用します。この方法の場合、ザイン システムに合わせてコンポーネントをカスタマイズしたり、ライブラリに含まれていないコンポーネントが必要になったりするまではうまく機能しますが、自分でコンポーネントに対して修正を加えたくなった場合、コンポーネントをラップしたり、スタイルをオーバーライドするための回避策を記述したり、互換性のない API を持つ異なるライブラリのコンポーネントを混在させたりすることになります。

一方shadcn/uiでは、実際のコンポーネント コードを自分のプロジェクトに落としてきて扱います。そのため、コンポーネントをカスタマイズおよび拡張する完全な制御権を持つことができ、コンポーネントを自分の要件に合わせて修正することが容易になります。

また生成AIに対応している点を特徴として謳っており、生成AIがshadcn/uiのコンポーネントを組み合わせて自然言語にあったwebデザインを作成するのはもちろん、shadcn/uiをベースに新しいコンポーネントの生成を行えるようなことを想定しています。

実際に先日投稿した【React】プロンプトからNext.jsアプリを自動生成!v0を試してみた!で使用したv0ではshadcn/uiを利用してuiを作成しているようです。

公式ドキュメント : https://ui.shadcn.com/docs

インストール

今回はNext.jsで導入する方法を確認していきます。

公式ガイド : https://ui.shadcn.com/docs/installation/next

npm,pnpm,yarn,bunのいずれかを使用してinitコマンドを走らせます。今回私はbunを利用しました。

❯ bunx --bun shadcn@latest init

各種設定を聞かれるので答えていきます。

✔ Which style would you like to use? › New York
✔ Which color would you like to use as the base color? › Neutral
✔ Would you like to use CSS variables for theming? … no / yes

これで完了です!

注意点

2025/02現在、Next.js or React19を利用していてnpmを利用している場合、 --force or  --legacy-peer-deps のフラグが必要になるようです。npmを利用している方はご注意を!

npm i <package> --force

npm i <package> --legacy-peer-deps

公式ガイド : Using shadcn/ui with Next.js 15 and React 19.

Buttonコンポーネントを使ってみる

では実際に利用してみましょう。まずはチュートリアルにもあるButtonコンポーネント利用する方法を確認してみます。

shadcn/uiはコンポーネントコレクションのため、一般的なコンポーネントライブラリでよくあるライブラリからButtonコンポーネントを呼び出してimportするといったことができません。使用するには使用したいコンポーネントを追加して上げる必要があります。追加するためにまずは以下のコマンドを実行してみましょう。

bunx --bun shadcn@latest add button

そうすると、デフォルトだとcomponents/ui直下にbutton.tsxが作成されます。そしてこのbutton.tsxを呼び出すことでButtonコンポーネントを使用することが可能です。page.tsxに以下のように記述してnext.jsを動かし、表示を確認してみましょう。

import { Button } from "@/components/ui/button";

export default function Home() {
  return (
    <div>
      <Button>Click me</Button>
    </div>
  );
}

無事表示することが出来ました!これがshadcn/uiの基本的な使用方法です。

Buttonコンポーネントのスタイルを変更してみる

ここでButton.tsのコードの一部を確認してみます。

・・・

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-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",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

・・・

中を確認するとvariantsというものがあり、variantと、sizeというプロパティがあります。これらを変更することで、コンポーネントに当てるスタイルを変更することが出来ます。今回はvariantに”outline”, sizeに”lg”を当ててみました。

      <Button variant="outline" size="lg">
        Click me
      </Button>

無事スタイルが変更されました!ここまでは一般的なコンポーネントライブラリでも利用できることの多い機能ですね。

Buttonコンポーネントをカスタマイズする

次は実際に自分なりのカスタマイズをコンポーネントに加えてみましょう。全体的にボタンが赤くなる”red”variantを加えてみます。

      variant: {
        default:
          "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",
        red: "bg-red-500 text-white shadow-sm hover:bg-red-600", #追加
      },

そうすると無事ボタンが赤くなりました!このコンポーネントの直接編集ができるのがコンポーネントコレクションであるメリットですね!

aschild

shadcn/uiではradix-uiのSlotを利用してasChild パターンというものが利用されています。asChildを利用すると、

  • asChild false のときデフォルトとして指定したコンポーネントをレンダリングする
  • asChild true のとき渡した子要素をレンダリングする

といったことが可能になります。

adChildパターンの理解に関してはこちらのブログを参考にさせていただきました。

asChild Pattern

今回は以下のコードを利用して

  1. Buttonコンポーネントをそのまま利用した場合
  2. asChildを利用せずにButtonコンポーネントの子要素としてaタグを渡した場合
  3. asChildを利用してButtonコンポーネントの子要素としてaタグを渡した場合

の挙動を確認します。

import { Button } from "@/components/ui/button";

export default function Home() {
  return (
    <div className="flex flex-col items-end  justify-center h-screen space-y-4">
      <Button>ボタンそのまま</Button>

      <Button>
        <a href="<https://google.com>">そのままaタグを渡す</a>
      </Button>

      <Button asChild>
        <a href="<https://google.com>">asChildでaタグを渡す</a>
      </Button>
    </div>
  );
}

1つ目のButtonコンポーネントをそのまま利用した場合、buttonタグとしてそのまま出力されます。

2つ目のasChildを利用せずにButtonコンポーネントの子要素としてaタグを渡した場合では、buttonタグの子要素として <a href=”https://google.com“>そのままaタグを渡す</a> が扱われる形になります。

3つ目のasChildを利用してButtonコンポーネントの子要素としてaタグを渡した場合では、直接aタグに対してスタイルやhref要素がマージされて扱うことができるようになります。

終わりに

今回はshadcn/uiについてご紹介しました。実際に使用してみている感想としてはコードを直接変更できることによる取り回しの良さが最高です。またデフォルトのデザインでもおしゃれかつ、複数のvariantが用意されているので、そのままでも使っていけると感じています。また、新しいコンポーネントも追加されているみたいですので、これからにも期待大ですね!

ではまた~

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です