Link and Motivation Developers' Blog

リンクアンドモチベーションの開発者ブログです

shadcn/uiの紹介しつつ今後のUIコンポーネントのつくり方を妄想してみる

UIをどうつくっていくか?

リンクアンドモチベーションでエンジニアをしています、宮田と申します。

自分はプロダクトのWebのUIを開発する時によくこんなことを考えていました。

「一からUIコンポーネントを開発する余裕はないなぁ」

「けどmuiのようなUIライブラリをそのまま使いたくない。ユースケースによってはUIに独自性を持たせたいなぁ」

最近だと「Tailwind CSSを使いたいなぁ」という考えも加わりました。

企業によっては強力なUIライブラリを社内で公開していて、それを使うことができるケースもあります。 ないケースや個人開発時は、毎回色々試行錯誤をしていたのですが、最近自分の中での最適解を見つけられた気がしたので紹介します。

自分の試行錯誤の変遷

※ 本記事ではReactを使う前提で書いています。

※ 1つのプロジェクトのみで試行錯誤しているのではなくて複数のプロジェクトで試行錯誤しています。

1. MUIのような多くのコンポーネントを内蔵したライブラリを使う

楽です。ですがカスタマイズが難しくデザインに拘りたい時にはカスタマイズが難儀です。 自分の実装力の向上に伴い使うことがなくなっていきました。

2. Tailwind CSS と Tailwind UI を使う

賛否がありますが、Tailwind CSSを好んで使います。

好きな理由は以下です。

  • スタイルをファイル単位で分割しなくて済む
  • インラインで書けて読みやすいこと
  • CSSのクラス名を考えなくて済む
  • スニペットがすぐに見つかる
  • 余白のpx数などに悩みづらい

Tailwind CSSの開発元の Tailwind Labsでは、Tailwind UIというTailwind CSSを使ったテンプレートを有償で販売しています。

Personalプランで$299、Teamsプランで$799しますが、良いUIをつくれるなら安いものだと購入(会社負担)して、Tailwind UIをTailwind CSSで拡張しながら使っていました。

しかしながら、Tailwindの責務は見た目のみで振る舞いについては自分自身で実装する必要があります。

最初は意気揚々とコンポーネントを開発していたのですが段々と車輪の再発明が面倒になってきました。

3. Tailwind CSS と MantineUI を使う

Tailwind CSSで拡張できる良い感じのUIライブラリないかなと探しているとMantineUIというものを見つけました。

MantineUIはStyles APIを提供しており、コンポーネントの任意の要素のスタイルを上書きすることができます。

MantineUIはデフォルトのスタイルが洗練されており気に入っていたのですが、Tailwind CSSのリセットCSSとMantineが競合してしまう問題があったり、メンテナがTailwind CSSを好きではないのが懸念ポイントでした。

I'm not planning to migrate Mantine to tw. It is not something that you or anybody else can convince me to do. I just do not like it – no matter how much tool is fast and popular, it still does not worth it if I would feel unhappy about working on the project with it.

https://github.com/mantinedev/mantine/issues/2815#issuecomment-1510283315

4. Tailwind CSS と shadcn/ui を使う

いくつかのライブラリを試した後に、自分が欲しかったものを整理してみました。

自分が求めていたものは、

  • よくあるコンポーネントが揃っていて見た目と挙動の実装をしなくても良い
  • 必要に応じて自分でカスタマイズできる
  • できればTailwind CSSと親和性が高いものが良い

でした。

それを叶えてくれるものがshadcn/uiでした。

ちなみに開発者のshadcnさんは Design EngineerとしてVercelに入社しているようです。

shadcn/ui の特徴

自分はTailwind CSSと相性の良いヘッドレスUIが欲しかったのだと思います。 shadcn/uiはまさにそれで、代表的なヘッドレスUIであるRadix UIに依存しています。

shadcn/uiのイントロダクションの最初には以下のように書いてあります。

Introduction Re-usable components built using Radix UI and Tailwind CSS. This is NOT a component library. It's a collection of re-usable components that you can copy and paste into your apps. What do you mean by not a component library? I mean you do not install it as a dependency. It is not available or distributed via npm. Pick the components you need. Copy and paste the code into your project and customize to your needs. The code is yours. Use this as a reference to build your own component libraries.

重要な特徴は、

だと思います。

※ Tailwindの開発元が出しているHeadless UIやMUIが出しているBase UIという選択肢もあったのですが提供コンポーネントが多くなく採用は見送りました。

shadcn/ui の使い方

shadcn/uiはドキュメントも非常に丁寧に書かれている印象です。

インストールも設定も公式ドキュメント通りで困ることはなかったです。

shadcn/uiの面白いところはコンポーネントを個別にインストールできることと、インストール先がnode_modules配下ではないことです。

例えばButtonコンポーネントをインストールするとします。

npx shadcn-ui@latest add button

するとインストール先は、例えばNext.jsのデフォルトだと、app/components/ui/になります。

├── app
│   ├── layout.tsx
│   └── page.tsx
├── components
│   ├── ui
│   │   ├── button.tsx
│   │   └── ...

app/components/ui/button.tsxのファイルは以下のようになっており。Tailwind CSSでスタイリングされています。

自由にスタイルは変更できるし、Propsを追加することも自由です。素晴らしいです!

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 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",
  {
    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-transparent 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",
    },
  }
)

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 }

今後のUIはどうつくられていくのか?

shadcn/uiを使っていると下手にデザインシステムを構築するよりも、高機能なUIをすぐに実装できるなと感じています。アクセシビリティも一定担保してくていますし巨人の肩に乗っている感じがすごいです。

Tailwind CSSの設定も丁寧に行えばデザイントークンが崩れることも少ないです。

もちろん複雑なユースケースには対応できませんが、ベースラインを整えるのは楽になったと感じます。まだWeb業界に入って9年目ですが、どんどん効率化していることを感じます。

最近はfigmaからTailwind CSSベースのコンポーネントを生成してくれたり、

velocity.builder.io

チャット形式でUIをオーダーできたりするツール(Private Alpha版なので使えないです)も出てきています。

v0.dev

Webが生まれた時はおそらくエンジニアとデザイナーが分かれていなかったはずです。UIの開発が簡単になった結果、また2つが統合したりするんですかね?

技術の進化を楽しみつつ、未来を妄想しつつ、変化を楽しんで行けたら良いなと思っています。