JSXは、コードの中にHTMLに似たマークアップを書けるようにするJavaScriptの構文拡張です。Reactコンポーネントは、画面に何を表示するかを表すためにJSXを返し、ビルドツールがそれを普通のJavaScriptの関数呼び出しにコンパイルします。見た目はHTMLですが、いくつかのルールが異なり、波かっこを使えば任意のJavaScriptの値をマークアップに入れられます。
{} の中はすべてJavaScriptです。プロパティ、メソッド呼び出し、さらには配列をリスト項目に変える map まで書けます。skills 配列に 'design' を追加すると、件数とリストの両方が更新されます。
JSXのルール
ルート要素は1つだけ返す
コンポーネントは1つの値を返すので、そのJSXには親が1つだけ必要です。トップレベルに兄弟要素が2つあると構文エラーになります。ページに余分な <div> を入れたくないときは、フラグメント <>...</> で囲みます。
// Error: Adjacent JSX elements must be wrapped in an enclosing tag
return (
<h1>Title</h1>
<p>Text</p>
);
// OK: a fragment groups them without adding a DOM element
return (
<>
<h1>Title</h1>
<p>Text</p>
</>
);
グループに key が必要なときは、長い形の <Fragment key={id}> を使います。フラグメントを参照してください。
すべてのタグを閉じる
HTMLでは一部のタグを閉じずにおけますが、JSXではできません。子を持たない要素は /> で自分を閉じます。<img />、<br />、<input />、そして <Avatar /> のような自分のコンポーネントもです。
className、htmlFor、キャメルケースの属性を使う
JSXの属性はJavaScriptオブジェクトのキーになるので、JavaScriptの命名規則に従います。class と for はJavaScriptの予約語なので、JSXでは className と htmlFor を使います。複数の単語からなる属性とすべてのイベントハンドラはキャメルケースです:tabIndex、maxLength、autoFocus、onClick、onChange。例外が2つあり、aria-* と data-* 属性はHTMLとまったく同じくハイフン付きで書きます。
className="field" を class="field" に変えてみてください。Reactはどちらにしても属性を設定するのでラベルのスタイルは保たれますが、開発ビルドでは「Invalid DOM property class. Did you mean className?」と記録されます。プレビューは本番ビルドのように動くので、ここではその警告は表示されません。className を使ってください。文字列の属性は引用符を使い(type="email")、それ以外の値は波かっこを使います(maxLength={40}、disabled={false})。
波かっこに書けるのは式で、文ではない
{} の中には、値を生み出すものなら何でも書けます。変数、計算、関数呼び出し、三項演算子、&&、テンプレートリテラルなどです。if、for、const のような文は値を生み出さないので、そこには書けません。処理は return の前に済ませるか、式の形を使ってください。
クリックする前によく見てください。ページに余計な 0 が表示されています。count && <p>...</p> はカウントが0のとき 0 に評価され、Reactは数値を、0であっても描画します。これを count > 0 && <p>You have mail.</p> に変えると 0 は消えます。false、null、undefined、true は何も描画しないので、&& の左側が真偽値なら安全です。パターンは条件付きレンダリングのページで扱っています。
インラインスタイルはオブジェクトで書く
style 属性が受け取るのはCSSの文字列ではなく、JavaScriptのオブジェクトです。そのため「二重の波かっこ」に見えます。外側の組は「ここはJavaScript」という意味で、内側の組がオブジェクトリテラルです。プロパティ名はキャメルケース(backgroundColor、fontSize)で、ほとんどのサイズ系プロパティでは単なる数値がピクセルを意味します。
スタイルはオブジェクトなので、ほかの値と同じようにstateから計算できます。fontSize: big ? 24 : 16 を fontSize: big ? '2em' : '1em' に変えてみてください。文字列は単位付きのまま渡されます。
コメント
JSXの中のコメントは波かっこの中に書きます:{/* this is a comment */}。HTMLのコメント(<!-- -->)は構文エラーです。マークアップの外、つまり関数本体では、普通の // コメントがいつもどおり使えます。
return (
<div>
{/* The header is hidden on small screens */}
<Header />
</div>
);
HTMLをJSXに変換する
HTMLをコンポーネントに貼り付けるのはよくある最初の一歩で、ほとんどはそのまま動きます。たいてい必要になる変更は次のとおりです。
classはclassNameに、forはhtmlForになります。- 空要素は自分で閉じます:
<br>は<br />に、<img src="a.png">は<img src="a.png" />になります。 style="color: red; font-size: 14px"はstyle={{ color: 'red', fontSize: 14 }}になります。- イベント属性は文字列ではなく関数を受け取ります:
onclick="save()"はonClick={save}になります。 - 属性名はキャメルケースになります:
tabindexはtabIndexに、SVGのstroke-widthはstrokeWidthになります。 <!-- comments -->は{/* comments */}になります。- トップレベルに複数の要素があるときは、親を1つにするかフラグメントで囲みます。
オンラインのHTMLからJSXへの変換ツールがこれらの手順をやってくれますが、知っておけば、どれかを忘れたときのエラーメッセージが読みやすくなります。
JSXのコンパイル結果
ブラウザはJSXを実行できません。コンパイラ(BabelやSWC、Oxcなど。Viteやフレームワークが代わりに実行します)が、各タグを react/jsx-runtime の関数呼び出しに変えます。
// What you write
const element = <h1 className="title">Hello, {name}</h1>;
const button = <Button size="small">OK</Button>;
// What the compiler produces (roughly)
import { jsx as _jsx, jsxs as _jsxs } from 'react/jsx-runtime';
const element = _jsxs('h1', { className: 'title', children: ['Hello, ', name] });
const button = _jsx(Button, { size: 'small', children: 'OK' });
小文字のタグは文字列('h1'、HTML要素)になり、大文字で始まるタグは自分の関数への参照(Button)になります。属性はpropsオブジェクトになり、中身は children になります。古い環境では代わりに React.createElement(type, props, ...children) にコンパイルされていました。そのため古いファイルは、React に一度も触れていなくても import React from 'react' で始まっています。この呼び出しはReact要素と呼ばれる普通のオブジェクトを返します。ログに出してみることもできます。
コンソールを見ると、heading は単なるデータで、型 'h3' とpropsオブジェクトだとわかります。だからこそJSXは変数に入れたり、関数に渡したり、関数から返したりできるのです。
JSXはテキストをエスケープする
波かっこに入れた文字列は、HTMLとしてではなく必ずテキストとして描画されます。ユーザーがコメント欄に <img src=x onerror=alert(1)> と入力しても、Reactは画像タグを作らず、その文字を画面に表示します。これにより、最もよくある形のクロスサイトスクリプティング(XSS)がデフォルトで防がれます。
1つ目の段落はタグを普通の文字として表示します。2つ目は dangerouslySetInnerHTML を使っていて、エスケープを省略して文字列を本物のHTMLとして挿入するので、「our own」が太字になります。長い名前は警告です。自分で生成したHTMLか、DOMPurifyのようなサニタイザーで無害化したHTMLだけを渡してください。そこにユーザーの入力を渡すと、JSXが塞いだXSSの穴をまた開けてしまいます。
よくある質問
JSXは何の略ですか?
JSXはJavaScript XMLの略です。JavaScriptのコードの中に、HTMLのように見えるマークアップを書けるようにする構文拡張です。
JSXはHTMLですか?
いいえ。JSXはHTMLのように見えますが、React要素を作るJavaScriptの関数呼び出しにコンパイルされます。そのため class の代わりに className を使い、属性名はキャメルケースで、すべてのタグを閉じる必要があります。
JSXなしでReactを使えますか?
はい。JSXは必須ではありません。react から createElement('h1', { className: 'title' }, 'Hello') を直接呼ぶこともできます。それでもネストしたマークアップがずっと読みやすくなるので、ほぼすべてのReactのコードベースがJSXを使っています。
JSXの中でif文を使えないのはなぜですか?
JSXの波かっこが受け付けるのは式、つまり値を生み出すものですが、if は文だからです。三項演算子(cond ? a : b)や && を使うか、return の前に値を変数に計算しておいてください。
JSXでコメントはどう書きますか?
JavaScriptのコメントを波かっこで囲みます:{/* like this */}。HTMLのコメント(<!-- -->)はJSXでは構文エラーになります。