JSX の基本

JSX は React で UI を記述するための構文拡張です。 HTML に似た見た目ですが、実際には JavaScript の構文糖衣(シンタックスシュガー)です。 この章では、JSX の書き方、ルール、式の埋め込み、コンパイルの仕組みを学びます。

JSX とは

JSX(JavaScript XML) は、JavaScript の中に HTML のようなマークアップを書くための構文拡張です。 React コンポーネントの return 文の中で使われ、 どのような UI をレンダリングするかを直感的に記述できます。

JSX の例
function Greeting() {
  const name = 'React';

  return (
    <div>
      <h1>こんにちは、{name}</h1>
      <p>JSX を使って UI を構築しています。</p>
    </div>
  );
}

JSX は HTML ではない

JSX は見た目こそ HTML に似ていますが、実際には React.createElement() 関数の呼び出しに変換される JavaScript の式です。 ブラウザが直接 JSX を理解することはできないため、ビルド時に Vite(内部で Babel / SWC を使用)が JSX を通常の JavaScript に変換します。

JSX が変換される様子
// 開発者が書く JSX
const element = <h1 className="title">Hello</h1>;

// ビルドツールが変換した結果(実際にブラウザで実行される)
const element = React.createElement(
  'h1',
  { className: 'title' },
  'Hello'
);

React 17 以降では、新しい JSX トランスフォームにより import React from 'react' を 各ファイルに書く必要がなくなりました。Vite のテンプレートではこの新しいトランスフォームが デフォルトで有効になっています。

JSX のルール

JSX には HTML とは異なるいくつかの重要なルールがあります。

ルール 1: 単一のルート要素を返す

JSX では、コンポーネントが返す要素は必ず 1 つのルート要素で囲む必要があります。 複数の要素を並列で返すことはできません。

NG: 複数のルート要素
// エラーになる!
function BadComponent() {
  return (
    <h1>タイトル</h1>
    <p>本文</p>
  );
}
OK: div で囲む
function GoodComponent() {
  return (
    <div>
      <h1>タイトル</h1>
      <p>本文</p>
    </div>
  );
}
OK: フラグメントで囲む(余分な DOM を増やさない)
function GoodComponent() {
  return (
    <>
      <h1>タイトル</h1>
      <p>本文</p>
    </>
  );
}

ルール 2: すべてのタグを閉じる

HTML では <img><br> などの自己閉じタグを閉じなくても動作しますが、 JSX ではすべてのタグを明示的に閉じる必要があります。

HTML vs JSX のタグの閉じ方
<!-- HTML: 閉じなくても OK -->
<img src="photo.jpg">
<br>
<input type="text">

{/* JSX: 必ず閉じる */}
<img src="photo.jpg" />
<br />
<input type="text" />

ルール 3: camelCase で属性を書く

JSX は JavaScript に変換されるため、HTML の属性名は JavaScript のオブジェクトプロパティになります。 JavaScript の予約語と衝突する属性名や、ハイフン区切りの属性名は camelCase に変更されています。

HTML の属性 JSX の属性 理由
class className class は JavaScript の予約語
for htmlFor for は JavaScript の予約語
tabindex tabIndex camelCase に統一
onclick onClick camelCase に統一
maxlength maxLength camelCase に統一
readonly readOnly camelCase に統一

最もよくあるミスが classclassName の混同です。 class と書いてもエラーにはなりませんが、コンソールに警告が表示されます。 常に className を使いましょう。

式の埋め込み

JSX の中で 波括弧 {} を使うと、JavaScript の式を埋め込むことができます。 変数の値、計算結果、関数の戻り値など、値を返すものなら何でも埋め込めます。

式の埋め込みの例
function UserProfile() {
  const name = '田中太郎';
  const age = 25;
  const hobbies = ['読書', 'プログラミング', 'ドラム'];

  return (
    <div>
      {/* 変数の埋め込み */}
      <h2>{name}</h2>

      {/* 計算式の埋め込み */}
      <p>来年の年齢: {age + 1}</p>

      {/* メソッドの戻り値 */}
      <p>趣味: {hobbies.join('、')}</p>

      {/* テンプレートリテラルも使える */}
      <p>{`${name} さんは ${age} 歳です`}</p>

      {/* 関数の呼び出し */}
      <p>現在時刻: {new Date().toLocaleTimeString()}</p>
    </div>
  );
}

埋め込めるもの・埋め込めないもの

埋め込み可能
文字列・数値 {'Hello'} / {42}
変数 {name}
計算式 {price * 1.1}
関数の呼び出し {formatDate(today)}
三項演算子 {isAdmin ? '管理者' : 'ユーザー'}
配列の map {items.map(item => <li>{item}</li>)}
埋め込み不可 理由
オブジェクト({{}} オブジェクトはそのまま表示できない([object Object] になる)
if if は文であり式ではない。三項演算子を使う
for for は文であり式ではない。map() を使う

式(expression)と文(statement)の違いを覚えておきましょう。 式は値を返すもの(1 + 2namea ? b : c)で、 文は値を返さない命令(ifforconst x = 1)です。 JSX の {} 内には式しか書けません。

条件付き表示

JSX の中で条件によって表示を切り替えたい場合、if 文は使えないため、 三項演算子&& 演算子を使います。

三項演算子(2 パターンの切り替え)

三項演算子による条件分岐
function UserStatus({ isLoggedIn }) {
  return (
    <div>
      {isLoggedIn
        ? <p>ようこそ!</p>
        : <p>ログインしてください。</p>
      }
    </div>
  );
}

&& 演算子(表示 / 非表示の切り替え)

条件が true の時だけ表示し、false の時は何も表示しない場合は && 演算子が便利です。

&& 演算子による条件表示
function Notification({ messages }) {
  return (
    <div>
      <h2>通知</h2>
      {/* messages が 0 件でなければ表示 */}
      {messages.length > 0 && (
        <p>未読メッセージが {messages.length} 件あります。</p>
      )}
    </div>
  );
}

&& の左辺に数値 0 を使わないように注意してください。 {count && <p>表示</p>} と書くと、count0 の時に 「何も表示しない」のではなく 0 という数字がレンダリングされます。 {count > 0 && <p>表示</p>} のように比較演算子を使いましょう。

スタイルの適用

className の使い方

JSX では CSS クラスを className 属性で指定します。 外部 CSS ファイルにスタイルを定義し、コンポーネントで import して使うのが基本的なパターンです。

src/components/Button.css
/* CSS ファイル */
.btn {
  padding: 8px 16px;
  border-radius: 4px;
  border: none;
  cursor: pointer;
}

.btn-primary {
  background-color: #0d6efd;
  color: white;
}

.btn-danger {
  background-color: #dc3545;
  color: white;
}
src/components/Button.jsx
import './Button.css';

function Button({ variant, children }) {
  return (
    <button className={`btn btn-${variant}`}>
      {children}
    </button>
  );
}

// 使用例
<Button variant="primary">送信</Button>
<Button variant="danger">削除</Button>

インラインスタイル

JSX でインラインスタイルを指定する場合は、文字列ではなく JavaScript オブジェクト を渡します。 プロパティ名は CSS のハイフン区切りではなく camelCase で書きます。

インラインスタイルの書き方
// HTML のインラインスタイル
// <div style="background-color: blue; font-size: 16px;">

// JSX のインラインスタイル(オブジェクトで渡す)
const style = {
  backgroundColor: 'blue',     // camelCase
  fontSize: '16px',             // camelCase
  padding: '10px 20px',
};

<div style={style}>スタイル適用</div>

// 直接オブジェクトを渡す場合(二重波括弧に注意)
<div style={{ color: 'red', fontWeight: 'bold' }}>
  赤い太字
</div>

二重波括弧 {{}} は特別な構文ではありません。 外側の {} が「JSX で JavaScript の式を埋め込む」ための記号で、 内側の {} が「JavaScript のオブジェクトリテラル」です。

インラインスタイルは動的にスタイルを変更したい場合に便利ですが、 基本的には CSS ファイル(または CSS Modules)を使うことを推奨します。 インラインスタイルは疑似クラス(:hover 等)やメディアクエリが使えないためです。

JSX のコンパイル

JSX はブラウザが直接理解できないため、ビルドツール(Vite / Babel / SWC)によって 通常の JavaScript に変換(トランスパイル)されます。 変換の仕組みを理解しておくと、JSX のルールがなぜ存在するのかが分かります。

JSX とその変換結果の比較
// ======= 開発者が書く JSX =======
function Card() {
  return (
    <div className="card">
      <h2>タイトル</h2>
      <p>本文テキスト</p>
    </div>
  );
}

// ======= 変換後の JavaScript =======
function Card() {
  return React.createElement(
    'div',
    { className: 'card' },
    React.createElement('h2', null, 'タイトル'),
    React.createElement('p', null, '本文テキスト')
  );
}

この変換の仕組みから、JSX のルールの理由が理解できます。

ルール 理由
単一のルート要素 return 文は 1 つの値しか返せないため、1 つの createElement 呼び出しに変換する必要がある
タグを閉じる JSX パーサーがタグの開始と終了を正しく解析するために必要
camelCase 属性 属性は JavaScript オブジェクトのプロパティになるため、JavaScript の命名規則に従う

フラグメント

フラグメント(Fragment) は、余分な DOM ノードを追加せずに 複数の要素をグループ化するための仕組みです。

React.Fragment の正式な記法
import { Fragment } from 'react';

function UserInfo() {
  return (
    <Fragment>
      <dt>名前</dt>
      <dd>田中太郎</dd>
      <dt>年齢</dt>
      <dd>25 歳</dd>
    </Fragment>
  );
}
省略記法(<> </>)
function UserInfo() {
  return (
    <>
      <dt>名前</dt>
      <dd>田中太郎</dd>
      <dt>年齢</dt>
      <dd>25 歳</dd>
    </>
  );
}

フラグメントは以下のような場面で役立ちます。

省略記法 <></> では key 属性を指定できません。 リストのレンダリングで key が必要な場合は、正式な <Fragment key={id}> を使用してください。

以上が JSX の基本ルールです。次の章では、React の核心であるコンポーネントについて詳しく学びます。