1. ホーム
  2. React

【React】children prop の使い方|コンポーネントの中身を差し込んで再利用する

Share

React でコンポーネントを作っていると、「見た目の枠は共通で、中身だけ差し替えたい」という場面が必ず出てきます。カード、モーダル、共通レイアウトなどがその代表です。これを実現するのが children という特別な prop です。この記事では、コンポーネントのタグで囲んだ中身がどうやって渡るのか、children を受け取る書き方、TypeScript での型の付け方、そして応用的な使い方までを、初心者にもわかりやすく解説します。props の基本を押さえたうえで読むと、より理解が深まります。

children とは「タグで囲んだ中身」のこと

ふだん HTML では、<div>ここに中身</div> のようにタグで内容を囲みます。React の自作コンポーネントでも同じことができ、開始タグと終了タグで囲んだ中身は、そのコンポーネントに children という名前の prop として自動的に渡されます。

Card.tsx
// children を受け取って、枠の中に表示する
function Card({ children }: { children: React.ReactNode }) {
  return <div className="card">{children}</div>;
}

// 使う側:タグで囲んだ中身が children として渡る
function App() {
  return (
    <Card>
      <h2>タイトル</h2>
      <p>これはカードの本文です。</p>
    </Card>
  );
}

<Card></Card> で囲んだ <h2><p> が、Card の中の {children} の位置にそのまま表示されます。Card は「枠」だけを提供し、中身は使う側が自由に決められる——これが children の基本的な考え方です。

なぜ children を使うのか

「中身も props で渡せばいいのでは?」と思うかもしれません。たしかに文字列1つなら props で十分です。しかし、渡したいのが見出し+本文+ボタンのような複数の要素のまとまりだったり、さらにその中に別のコンポーネントが入っていたりする場合、それを全部 props で受け渡すのは大変です。children なら、HTML を書く感覚でそのまま囲むだけで渡せます。

この仕組みのおかげで、同じ枠を色々な中身で使い回せます。たとえば Card を1つ作れば、お知らせカード、プロフィールカード、商品カードと、中身を変えるだけで何にでも使えます。「見た目の共通部分」と「中身」を分離できるのが、children の大きな利点です。

reuse.tsx
// 同じ Card を、中身を変えて何度でも使える
function App() {
  return (
    <>
      <Card>
        <p>1つ目のカードです。</p>
      </Card>

      <Card>
        <h3>2つ目</h3>
        <button>ボタン</button>
      </Card>
    </>
  );
}

TypeScript での型の付け方

TypeScript で children を受け取るときは、その型を指定する必要があります。children には文字列、数値、JSX 要素、要素の配列など、画面に表示できるものなら何でも入るため、それらをまとめて表す React.ReactNode を使うのが基本です。

types.tsx
// props の型をまとめて定義する書き方
type CardProps = {
  children: React.ReactNode;
};

function Card({ children }: CardProps) {
  return <div className="card">{children}</div>;
}

React.ReactNode は、React で「表示できるもの」全般を表す型です。JSX 要素はもちろん、文字列や数値、null(何も表示しない)、それらの配列まで含みます。children の型に迷ったら、まず React.ReactNode を選んでおけば間違いありません。

children と他の props を組み合わせる

children は特別な名前というだけで、他の props と一緒に使えます。たとえば「タイトルは props で受け取り、本文は children で受け取る」といった設計にすると、枠の一部は固定しつつ、中身は柔軟に、というコンポーネントが作れます。

Panel.tsx
type PanelProps = {
  title: string;              // 見出しは props で
  children: React.ReactNode;  // 中身は children で
};

function Panel({ title, children }: PanelProps) {
  return (
    <section className="panel">
      <h2>{title}</h2>
      <div className="panel-body">{children}</div>
    </section>
  );
}

// 使う側
function App() {
  return (
    <Panel title="お知らせ">
      <p>本日のメンテナンスは完了しました。</p>
    </Panel>
  );
}

このように、title は通常の prop として属性で渡し、本文はタグの中身(children)として渡します。役割ごとに渡し方を分けることで、使う側にとって分かりやすいコンポーネントになります。

中身が渡されないときに備える

コンポーネントを空のタグで使うと、children は空になります。中身が無いときに代わりの表示を出したい場合は、children があるかどうかで分岐します。JSX では、値が入っていれば表示し、なければ別の内容を出す、といった書き方ができます。

fallback.tsx
function Box({ children }: { children?: React.ReactNode }) {
  return (
    <div className="box">
      {/* children があればそれを、なければ代わりの文言を表示 */}
      {children ?? <p>内容がありません</p>}
    </div>
  );
}

中身が省略される可能性があるときは、型を children?: React.ReactNode のように ? を付けて「あってもなくてもよい」と表しておくと、使う側で空にしても型エラーになりません。

まとめ

children は、コンポーネントのタグで囲んだ中身を受け取る特別な prop です。これを使うと、カードやモーダル、共通レイアウトのように「枠は共通・中身は自由」というコンポーネントを簡単に作れ、同じ枠を色々な中身で再利用できます。TypeScript では型に React.ReactNode を使うのが基本で、省略される可能性があるなら ? を付けます。title などの他の props と組み合わせれば、固定したい部分と差し替えたい部分を分けた、使いやすいコンポーネントになります。「見た目の共通化」を考え始めたら、まず children で中身を差し込めないかを検討してみましょう。

参考ページ