Next.jsのApp Routerを学ぶ際、shadcn/uiが参考になったという話

Next.jsのApp Routerを学ぶ際に、UIライブラリを探していたところ、shadcn/uiが参考になったので紹介します。

shadcn/uiは、フレームワークやライブラリではなく、コピペで利用できるReactコンポーネントのサンプル集という立ち位置になっています。Tailwind CSSでデザインされており、React Server Components(RSC)にも対応しています。
サンプルの実装方法が参考になる点も魅力の一つです。

簡単ですがつらつらと紹介していきたいと思います。

# shadcn/uiとは

デモを見てるとフレームワークやライブラリなのかなと思いきやそうではなく、コピペで利用できるサンプル集という立ち位置になっています。基本的にはTailwind CSSでデザインされてるようです。
サンプルとはいえ、実装方法で参考になった箇所が結構あったのでそこも気に入ってるポイントです。

https://ui.shadcn.com/docs

## 関連技術

- Next.js@14
- Radix UI
- Tailwind CSS
- React Server Components対応
- class-variance-authorityを積極的に活用
- ダークモード対応

# Example

サンプルが色々あるので、使いたい機能に合わせてコピペして持ってこれそうです。

https://ui.shadcn.com/examples/dashboard

![CleanShot 2024-05-10 at 17.28.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3442/8b4f432c-8a82-b3da-1bd3-290abe660f33.png)

# 導入方法

Next.jsにフォーカスして調べてはいたのですが、ViteやAstro等色々対応しているみたいです。

https://ui.shadcn.com/docs/installation

![CleanShot 2024-05-10 at 17.30.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3442/525a5207-f97e-0169-e52c-81ffeb34dbe5.png)


## セットアップ

今回はNext.jsのセットアップ方法を紹介します

```bash
% npx create-next-app@latest my-app --typescript --tailwind --eslint
% npx shadcn-ui@latest init
```


## 利用方法

ボタンのサンプルが欲しければ、こんな感じで持って来れます

```bash
% npx shadcn-ui@latest add button
```

```typescript
import { Button } from "@/components/ui/button"
 
export default function Home() {
  return (
    <div>
      <Button>Click me</Button>
    </div>
  )
}
```

## カスタマイズ

cvaでデザインのパターンが増やせる実装になっているので要件に合わせて、色やvariantを変更していくと良さそうです。具体的な実装例を貼っておきます。


```typescript:components/ui/button.tsx
import * as React from "react"
import { VariantProps, cva } 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-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background",
  {
    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 hover:bg-accent hover:text-accent-foreground",
        secondary:
          "bg-secondary text-secondary-foreground hover:bg-secondary/80",
        ghost: "hover:bg-accent hover:text-accent-foreground",
        link: "underline-offset-4 hover:underline text-primary",
      },
      size: {
        default: "h-10 py-2 px-4",
        sm: "h-9 px-3 rounded-md",
        lg: "h-11 px-8 rounded-md",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, ...props }, ref) => {
    return (
      <button
        className={cn(buttonVariants({ variant, size, className }))}
        ref={ref}
        {...props}
      />
    )
  }
)
Button.displayName = "Button"

export { Button, buttonVariants }

```

## 実装サンプル

Server Componentsの実装方法やライブラリの選定、デザインの管理方法などだいぶ参考になったのでこちらのサンプルも紹介します。


### ソース

https://github.com/shadcn-ui/taxonomy

### デモ

https://tx.shadcn.com/