shadcn/ui の Button コンポーネントを眺めてみる
こんにちは。Gaji-Labo 横田です。前回 shadcn/ui の導入について記事にしました。今回は shadcn/ui の Button コンポーネントのコードを眺めてみたいと思います。
Button コンポーネント
shadcn/ui のコンポーネントのインストールは CLI では add
コマンドで行います。
Button コンポーネントの場合は以下となります。
npx shadcn-ui@latest add button
プロジェクトで使用したいコンポーネントだけを選んでインストールできるのがいいですね。
ちなみに公式ドキュメントから手動でコードをコピペし、任意のディレクトリに Button コンポーネントファイルを作成することも可能です。その場合は先に依存関係のある radix-ui/react-slot をインストールしておく必要があります。手順は以下の Manual タブをご確認ください。
https://ui.shadcn.com/docs/components/button#installation
インストールが完了すると、Next.js 環境では src/app/components/ui
に button.tsx
が追加されています。
コードを眺めてみましょう。
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 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",
{
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",
},
}
)
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 }
buttonVariants
の定義でボタンの色とサイズのスタイルが複数パターン指定されています。bg-primary
など Tailwind CSS のデフォルトでは定義されていない class は、 tailwind.config.ts
ファイル内で指定されています。
次に ButtonProps
というインターフェースで Button コンポーネントの props を定義しています。React.ButtonHTMLAttributes<HTMLButtonElement>
で HTML の button 要素の全ての標準属性(たとえば disabled
や onClick
など)を含めています。VariantProps<typeof buttonVariants>
で buttonVariants
に定義されたバリエーションの props を受け取ることができるようになっています。オプショナルなプロパティ asChild
で Button
コンポーネントが子要素としてレンダリングされるべきかどうかを示しています。
最後に Button
コンポーネントの定義で React.forwardRef
を使用して ref を子コンポーネントに直接渡すことができます。asChild
prop で radix-ui の Slot コンポーネントまたは button 要素をレンダリングします。
radix-ui の Slot:https://github.com/radix-ui/primitives/blob/main/packages/react/slot/src/Slot.tsx
Button コンポーネントを使用する
ページでの使い方は簡単です。たとえばサイズが lg で secondary のスタイルをあてたボタンは以下のように指定するだけです。
import { Button } from "../components/ui/button";
export default function Button() {
return (
<Button variant="secondary" size="lg">Secondary</Button>
);
}
Button コンポーネントを a 要素(リンク)で使いたい時は以下のように asChild を true にしてリンクコンポーネントをネストできます。
import Link from "next/link"
import { Button } from "../components/ui/button"
export default function ButtonAsChild() {
return (
<Button asChild>
<Link href="/login">Login</Link>
</Button>
)
}
shadcn/ui を使うメリット
コードを眺めてみると、前回お伝えした利点「コードを自由にカスタマイズすることができる」「拡張のしやすさから、自作した UI コンポーネントとも共存させやすい」が理解できると思います。
radix-ui を元にスタイルのバリエーションが複数用意されたコンポーネントのテンプレートのようなものなので、プロジェクトに合わせた見た目のカスタマイズを(Button コンポーネントであれば buttonVariants
定義で)Tailwind CSS を使って追加・削除・変更・拡張することが容易です。
HTML の標準属性を props で渡せるので、アクセシビリティ対応などで属性を追記するのも容易です。
そしてコンポーネントのコードがそのまま露出していますので、shadcn/ui のお作法に寄せて、自作コンポーネントを作ることも難しくなさそうです。ライブラリの使用と自作コードの整合性をとりやすいのは、開発者にとってスピードやメンテナンス性における大きなメリットになると思います。
終わりに
今回は Button コンポーネントのコードを眺めてみました。公式ドキュメントを見ると、Pagination や Carousel など新しいコンポーネントが続々と追加されており、ますます導入しやすくなりそうです。
Gaji-Laboでは、React経験が豊富なフロントエンドエンジニアを募集しています
弊社ではReactの知見で事業作りに貢献したいフロントエンドエンジニアを募集しています。大きな制作会社や事業会社とはひと味もふた味も違うGaji-Laboを味わいに来ませんか?
もちろん、一緒にお仕事をしてくださるパートナーさんも随時募集中です。まずはお気軽に声をかけてください。お仕事お問い合わせや採用への応募、共に大歓迎です!
求人応募してみる!