Reactのコンポーネント設計 - React.ComponentProps 型が便利
開発環境
- react 19.2.0
- react-dom 19.2.0
前提
React のコンポーネント設計では、HTML 要素や既存コンポーネントの Props を安全かつ効率的に引き継ぎたい場面が多くあります。
その際、手で Props をすべて再定義するのは非効率で、保守性も低くなります。
これを解決する技法として有効なのが React.ComponentProps を使う設計です。
shadcn/ui のコンポーネントも同じ考え方を採用しており、現代的な UI コンポーネント設計における重要なパターンになっています。
本題
1. React.ComponentProps の概要
React.ComponentProps<"button"> のように指定すると、その要素が本来受け取るすべての Props(イベント、属性、ARIA、ref など)をそのまま型として利用できます。
type ButtonProps = React.ComponentProps<"button">;この仕組みによって、React コンポーネントで HTML 属性を再記述する必要がなくなります。
2. shadcn/ui が採用している Props 設計
shadcn/ui のコンポーネントでは、次の 2 点を組み合わせた設計になっています。
- React.ComponentProps<"button">
- VariantProps<typeof buttonVariants>
実際の Button コンポーネントは以下のような構造です。
function Button({
className,
variant,
size,
asChild = false,
...props
}: React.ComponentProps<"button"> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean
}) {
const Comp = asChild ? Slot : "button"
return (
<Comp
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
)
}この設計により、HTML ボタン本来の属性(onClick, disabled, type, aria-* など)を余さず受け継ぎつつ、UI ライブラリ独自のバリアント機能を組み合わせられます。
3. React.ComponentProps を使うメリット
👉 3-1. コード量が減らせる
onClick, disabled, type などを手動で列挙する必要がなくなるため、Props 定義が最小限で済みます。
// 冗長: 自前で列挙
type Props = {
onClick?: () => void
disabled?: boolean
type?: "button" | "submit"
}// 省略できる
type Props = React.ComponentProps<"button">;👉 3-2. 型の追従が自動化
HTML 要素側に新しい属性が追加されても、そのまま使えるため更新コストがほぼゼロになります。
👉 3-3. 独自定義との併用が簡単
shadcn/ui のように VariantProps と組み合わせると、以下を同時に満たせます。
- HTML 要素の完全な互換性
- UI デザインシステムとしてのバリアント管理
- Props の自然な拡張
React ComponentProps が基盤となっていることで、拡張性と保守性が高い設計になります。
👉 3-4. 「薄いラッパー」が作りやすい
HTML ボタンを装飾しただけのコンポーネントのように、元の DOM 属性をそのまま渡したいケースは多いです。 その際、Props の再定義が不要で、使い心地も素の HTML と一致するため、ラッパーコンポーネントを気軽に作れます。
さいごに
React.ComponentProps は、HTML 要素や既存コンポーネントの Props を安全に継承できる強力なユーティリティです。 shadcn/ui のような洗練されたコンポーネント設計にも採用されており、コード量削減・保守性向上・拡張性の改善に大きく寄与します。
コンポーネント設計の基礎として、ぜひ日常的に活用したいテクニックです。