React でコンポーネントを書いていると、「複数の要素を返したいのにエラーになる」「とりあえず <div> で囲んで解決したけれど、余計な div が積み重なってしまう」という悩みに出会います。React のコンポーネントは要素を1つだけ返すという決まりがあるため、並んだ複数の要素はまとめる必要があります。このときに、余分な HTML を一切増やさずにまとめられるのが Fragment(フラグメント)です。この記事では、Fragment がなぜ必要なのか、<></> の省略記法と <React.Fragment> の使い分け、リスト表示で key を付けたいときの書き方、そして余分な div がレイアウトを崩す問題までを、初心者向けに解説します。
目次
なぜ複数要素をそのまま返せないのか
まず、Fragment が必要になる理由を確認しましょう。次のように return の中で2つの要素を並べて返そうとすると、エラーになります。
function Profile() {
// エラー: 隣り合う複数の要素は1つにまとめる必要がある
return (
<h1>佐藤さん</h1>
<p>フロントエンドエンジニア</p>
);
}
JSX(JavaScript の中に HTML のような記述を書ける構文)は、内部的には1回の return で1つの値を返す関数呼び出しに変換されます。そのため、並んだ2つの要素をそのまま返すことはできません。もっとも手軽な解決策は <div> で囲むことですが、意味を持たない div がページ中に増えていくと、後述するようにレイアウトの妨げになることがあります。そこで Fragment の出番です。
Fragment で要素をまとめる
Fragment は、実際の HTML を出力せずに複数の要素をまとめるための特別なラッパーです。<React.Fragment> と書く方法と、それを短くした <></>(空タグ)という記法があります。普段は短い記法で十分です。
// 空タグ <>> でまとめる(推奨の書き方)
function Profile() {
return (
<>
<h1>佐藤さん</h1>
<p>フロントエンドエンジニア</p>
</>
);
}
<> と </> で囲むだけで、エラーは解消します。このとき DOM(実際に描画される HTML)には <h1> と <p> だけが並び、それらを包む余計な要素は一切出力されません。div で囲んだ場合との違いを、次の表で整理します。
| まとめ方 | DOM に出力される要素 |
|---|---|
<div>...</div> | div が1つ増える(中身を包む) |
<>...</>(Fragment) | 何も増えない(中身だけが並ぶ) |
短縮記法と React.Fragment の使い分け
<></> の短縮記法はとても便利ですが、1つだけできないことがあります。それは key 属性を付けることです。後述するリスト表示で key が必要な場面では、短縮記法ではなく <React.Fragment>(インポートすれば <Fragment>)と正式な形で書く必要があります。両者の違いはこれだけで、DOM に何も出力しない点は同じです。
import { Fragment } from 'react';
// 短縮記法: key は付けられない
const a = (
<>
<span>A</span>
<span>B</span>
</>
);
// 正式な記法: key などの属性を付けられる
const b = (
<Fragment key="group1">
<span>A</span>
<span>B</span>
</Fragment>
);
ふだんは短縮記法を使い、key が必要なときだけ <Fragment> を使う、という使い分けで問題ありません。
リスト表示で key を付けたいとき
map でリストを作るとき、1件につき複数の要素(たとえば <dt> と <dd>)を出力したいことがあります。React ではリストの各項目に key(各要素を見分けるための目印)が必要ですが、余計な div で包みたくありません。こういうときこそ、key を付けられる <Fragment> が役立ちます。
import { Fragment } from 'react';
type Term = { id: number; word: string; desc: string };
function Glossary({ terms }: { terms: Term[] }) {
return (
<dl>
{terms.map((t) => (
// 1件ごとに dt と dd をまとめ、key は Fragment に付ける
<Fragment key={t.id}>
<dt>{t.word}</dt>
<dd>{t.desc}</dd>
</Fragment>
))}
</dl>
);
}
<dl> の直下には <dt> と <dd> だけを置くのが正しい構造です。もしここで div を挟むと、定義リストの構造が崩れてしまいます。<Fragment key={t.id}> なら、余計な要素を出さずに key も付けられるため、この形にぴったり合います。
余分な div がレイアウトを崩すとき
テーブルの構造が壊れる
<table> の中では、<tr> の直下に <td> を並べるという構造が決まっています。ここでセルのまとまりを div で包むと、テーブルとして正しくない HTML になり、表示が崩れます。複数の <td> をまとめて返したいコンポーネントでは、div ではなく Fragment を使うことで構造を保てます。
Flexbox や Grid の直下の子が増える
親要素に display: flex や display: grid を指定していると、その直下の子要素が並びの対象になります。ここで意味のない div を1枚挟むと、本来横並びにしたかった要素がその div の中に入ってしまい、レイアウトが意図通りになりません。Fragment なら余計な階層を作らないので、Flexbox や Grid の子要素の並びをそのまま保てます。こうした「ラップ用の要素が邪魔になる」場面こそ、Fragment がもっとも役立つところです。
まとめ
React のコンポーネントは要素を1つしか返せないため、複数の要素は何かでまとめる必要があります。div で囲む方法は手軽ですが、余分な要素が増え、テーブルや Flexbox・Grid のレイアウトを崩すことがあります。Fragment を使えば、DOM に何も出力せずに要素をまとめられます。ふだんは短縮記法 <></> を使い、map でのリスト表示のように key が必要なときだけ <Fragment key={...}> を使う、という使い分けを覚えておきましょう。「まとめたいけれど余計な HTML は増やしたくない」ときの定番として、Fragment はとても役立ちます。