はじめに

前回の記事では、Viteを使ったReact開発環境の構築方法を解説しました。本記事では、ReactでUIを記述するための構文「JSX」について詳しく解説します。

JSXはReact開発において最も基本的かつ重要な概念です。HTMLに似た構文でUIを直感的に記述できますが、JavaScriptの一部として動作するため、独自のルールがあります。

本記事を読むことで、以下のことが理解できるようになります。

  • JSXの基本概念と仕組み
  • JSXにおけるJavaScript式の埋め込み方法
  • 条件分岐とループ処理の書き方
  • classNameやstyle属性の正しい使い方
  • JSXでよくある間違いとその対処法

実行環境・前提条件

必要な環境

  • Node.js 20.x以上
  • Viteで作成したReactプロジェクト
  • VS Code(推奨)

前提知識

  • HTMLの基本的な知識
  • JavaScriptの基礎(変数、関数、配列、オブジェクト)
  • 前回の記事で作成したReactプロジェクト

JSXとは何か

JSXは「JavaScript XML」の略で、JavaScriptの中にHTMLライクな構文を記述できる拡張構文です。

JSXの基本形

1
2
3
function Greeting() {
  return <h1>Hello, React!</h1>;
}

この<h1>Hello, React!</h1>の部分がJSXです。HTMLのように見えますが、実際にはJavaScriptのコードです。

JSXはJavaScriptに変換される

JSXはブラウザが直接解釈できる構文ではありません。ビルド時にBabelなどのトランスパイラによって、通常のJavaScriptに変換されます。

1
2
3
4
5
// JSX
const element = <h1>Hello, React!</h1>;

// 変換後のJavaScript
const element = React.createElement('h1', null, 'Hello, React!');

Viteでは、この変換が自動的に行われるため、開発者が意識する必要はありません。

なぜJSXを使うのか

JSXを使わずにReactを書くことも技術的には可能ですが、JSXを使用することで以下のメリットがあります。

  • 可読性の向上:UIの構造が視覚的に理解しやすい
  • 開発効率の向上:HTMLに近い記法で直感的に書ける
  • 型安全性:TypeScriptとの組み合わせでコンパイル時にエラーを検出
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// JSXを使わない場合(読みにくい)
React.createElement('div', { className: 'card' },
  React.createElement('h2', null, 'タイトル'),
  React.createElement('p', null, '本文テキスト')
);

// JSXを使う場合(読みやすい)
<div className="card">
  <h2>タイトル</h2>
  <p>本文テキスト</p>
</div>

JSXの基本ルール

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

ルール1:単一のルート要素

JSXでは、必ず単一のルート要素で全体を囲む必要があります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// エラー:複数のルート要素がある
function BadExample() {
  return (
    <h1>タイトル</h1>
    <p>本文</p>
  );
}

// 正しい:単一のルート要素で囲む
function GoodExample() {
  return (
    <div>
      <h1>タイトル</h1>
      <p>本文</p>
    </div>
  );
}

フラグメント(Fragment)の活用

余分なDOM要素を追加したくない場合は、React Fragmentを使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import { Fragment } from 'react';

// Fragmentを使用
function Example1() {
  return (
    <Fragment>
      <h1>タイトル</h1>
      <p>本文</p>
    </Fragment>
  );
}

// 省略構文(推奨)
function Example2() {
  return (
    <>
      <h1>タイトル</h1>
      <p>本文</p>
    </>
  );
}

<></>は空のタグのように見えますが、Fragmentの省略構文です。レンダリング時にはDOMに影響を与えません。

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

HTMLでは省略可能な閉じタグも、JSXでは必ず閉じる必要があります。

1
2
3
4
5
6
7
8
9
// HTMLでは許容されるがJSXではエラー
<input type="text">
<img src="image.png">
<br>

// JSXでは自己閉じタグを使用
<input type="text" />
<img src="image.png" />
<br />

ルール3:属性名はキャメルケース

HTMLの属性名は小文字ですが、JSXではキャメルケース(camelCase)を使用します。

1
2
3
4
5
// HTML
<button onclick="handleClick()" tabindex="0">クリック</button>

// JSX
<button onClick={handleClick} tabIndex={0}>クリック</button>

主な属性名の変換は以下のとおりです。

HTML属性 JSX属性
onclick onClick
onchange onChange
onsubmit onSubmit
tabindex tabIndex
readonly readOnly
maxlength maxLength

ルール4:classはclassNameに

classはJavaScriptの予約語であるため、JSXではclassNameを使用します。

1
2
3
4
5
// HTML
<div class="container">...</div>

// JSX
<div className="container">...</div>

同様に、for属性はhtmlForに変換します。

1
2
3
4
5
// HTML
<label for="email">Email:</label>

// JSX
<label htmlFor="email">Email:</label>

JavaScript式の埋め込み

JSXの最大の特徴は、JavaScriptの式を自由に埋め込めることです。

波括弧 {} による式の埋め込み

JSXの中で{}を使用すると、JavaScriptの式を埋め込めます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function Greeting() {
  const name = 'React';
  const year = 2025;
  
  return (
    <div>
      <h1>Hello, {name}!</h1>
      <p>現在は{year}年です</p>
      <p>来年は{year + 1}年です</p>
    </div>
  );
}

期待される結果

上記のコードをレンダリングすると、以下のように表示されます。

Hello, React!
現在は2025年です。
来年は2026年です。

式と文の違い

JSXの{}内に書けるのは「式(expression)」であり、「文(statement)」は書けません。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 式:値を返すもの
{1 + 2}           // 算術式
{name}            // 変数
{getName()}       // 関数呼び出し
{isActive ? 'Yes' : 'No'}  // 三項演算子

// 文:値を返さないもの(JSX内では使用不可)
{if (condition) { ... }}  // エラー
{for (let i = 0; i < 10; i++) { ... }}  // エラー
{const x = 10;}  // エラー

属性への式の埋め込み

属性値にも式を埋め込むことができます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
function UserImage() {
  const user = {
    name: 'Taro',
    imageUrl: 'https://example.com/avatar.png',
    imageSize: 100
  };
  
  return (
    <img
      src={user.imageUrl}
      alt={user.name + 'のアバター'}
      width={user.imageSize}
      height={user.imageSize}
    />
  );
}

文字列リテラルの場合は引用符を使用し、式の場合は波括弧を使用します。

1
2
3
4
5
// 固定の文字列はクォートで
<input type="text" placeholder="名前を入力" />

// 変数や式は波括弧で
<input type="text" placeholder={placeholder} />

条件分岐の書き方

JSXでは、条件によって異なるUIを表示する方法がいくつかあります。

三項演算子(条件 ? 真 : 偽)

最も一般的な条件分岐の方法です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function UserStatus({ isLoggedIn }) {
  return (
    <div>
      {isLoggedIn ? (
        <p>ようこそ</p>
      ) : (
        <p>ログインしてください</p>
      )}
    </div>
  );
}

論理AND演算子(&&)

条件が真の場合のみ表示する場合は、&&演算子を使用します。

1
2
3
4
5
6
7
function Notification({ hasUnread, count }) {
  return (
    <div>
      {hasUnread && <span className="badge">{count}件の未読</span>}
    </div>
  );
}

注意点count0の場合、0がそのまま表示されてしまいます。

1
2
3
4
5
// count が 0 の場合、「0」が表示される
{count && <span>通知: {count}</span>}

// 正しい書き方
{count > 0 && <span>通知: {count}</span>}

論理OR演算子(||)とNull合体演算子(??)

デフォルト値を設定する場合に使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function UserName({ name }) {
  return (
    <p>ユーザー名: {name || 'ゲスト'}</p>
  );
}

// nullまたはundefinedの場合のみデフォルト値を使用
function Price({ value }) {
  return (
    <p>価格: {value ?? '未設定'}</p>
  );
}

複雑な条件分岐

条件が複雑な場合は、JSXの外で変数に代入する方法が読みやすくなります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
function StatusMessage({ status }) {
  let message;
  
  if (status === 'loading') {
    message = <p>読み込み中...</p>;
  } else if (status === 'success') {
    message = <p>完了しました</p>;
  } else if (status === 'error') {
    message = <p>エラーが発生しました</p>;
  } else {
    message = <p>待機中</p>;
  }
  
  return <div>{message}</div>;
}

ループ処理とリストのレンダリング

配列のデータを一覧表示する方法を解説します。

map()メソッドによるリストの生成

配列の各要素に対してJSXを生成するには、map()メソッドを使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function FruitList() {
  const fruits = ['りんご', 'バナナ', 'オレンジ'];
  
  return (
    <ul>
      {fruits.map((fruit, index) => (
        <li key={index}>{fruit}</li>
      ))}
    </ul>
  );
}

期待される結果

1
2
3
4
5
<ul>
  <li>りんご</li>
  <li>バナナ</li>
  <li>オレンジ</li>
</ul>

key属性の重要性

リストの各要素には、一意のkey属性を指定する必要があります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
function UserList() {
  const users = [
    { id: 1, name: '田中太郎' },
    { id: 2, name: '鈴木花子' },
    { id: 3, name: '佐藤一郎' }
  ];
  
  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

keyに関するルール

  • 兄弟要素間で一意である必要がある
  • 可能な限り、データ固有のID(データベースのIDなど)を使用する
  • 配列のインデックスは、要素の順序が変わる可能性がある場合は避ける
1
2
3
4
5
// 良い例:一意のIDを使用
<li key={user.id}>{user.name}</li>

// 避けるべき例:インデックスを使用(順序が変わる可能性がある場合)
<li key={index}>{user.name}</li>

オブジェクトの配列を表示する

実際のアプリケーションでは、より複雑なデータ構造を扱います。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
function ProductList() {
  const products = [
    { id: 'p1', name: 'ノートPC', price: 150000, inStock: true },
    { id: 'p2', name: 'マウス', price: 3000, inStock: true },
    { id: 'p3', name: 'キーボード', price: 12000, inStock: false }
  ];
  
  return (
    <div className="product-list">
      {products.map((product) => (
        <div key={product.id} className="product-card">
          <h3>{product.name}</h3>
          <p>価格: {product.price.toLocaleString()}</p>
          <p>{product.inStock ? '在庫あり' : '在庫なし'}</p>
        </div>
      ))}
    </div>
  );
}

フィルタリングとの組み合わせ

filter()map()を組み合わせて、条件に合う要素のみを表示できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
function InStockProducts() {
  const products = [
    { id: 'p1', name: 'ノートPC', price: 150000, inStock: true },
    { id: 'p2', name: 'マウス', price: 3000, inStock: true },
    { id: 'p3', name: 'キーボード', price: 12000, inStock: false }
  ];
  
  return (
    <ul>
      {products
        .filter((product) => product.inStock)
        .map((product) => (
          <li key={product.id}>{product.name}</li>
        ))}
    </ul>
  );
}

styleの扱い方

JSXでは、インラインスタイルをオブジェクトとして記述します。

インラインスタイルの書き方

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function StyledBox() {
  return (
    <div style={{
      backgroundColor: '#f0f0f0',
      padding: '20px',
      borderRadius: '8px',
      boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
    }}>
      スタイル適用済みのボックス
    </div>
  );
}

重要なポイント

  • スタイルはオブジェクトとして渡す(二重の波括弧に注意)
  • プロパティ名はキャメルケース(background-colorbackgroundColor
  • 値は文字列または数値(数値の場合はpxが自動付与)

動的なスタイル

変数や条件に基づいてスタイルを変更できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function DynamicBox({ isActive, size }) {
  const boxStyle = {
    width: size,
    height: size,
    backgroundColor: isActive ? '#4CAF50' : '#9E9E9E',
    transition: 'background-color 0.3s'
  };
  
  return <div style={boxStyle}>ボックス</div>;
}

スタイルオブジェクトの分離

スタイルをコンポーネントの外で定義することも一般的です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const styles = {
  container: {
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center',
    minHeight: '100vh'
  },
  card: {
    backgroundColor: 'white',
    padding: '24px',
    borderRadius: '12px',
    boxShadow: '0 4px 6px rgba(0,0,0,0.1)'
  },
  title: {
    fontSize: '24px',
    fontWeight: 'bold',
    marginBottom: '16px'
  }
};

function Card() {
  return (
    <div style={styles.container}>
      <div style={styles.card}>
        <h1 style={styles.title}>カードタイトル</h1>
        <p>カードの内容</p>
      </div>
    </div>
  );
}

CSSファイルとの使い分け

インラインスタイルは動的なスタイルに適していますが、以下の場合はCSSファイルの使用を推奨します。

  • 擬似クラス(:hover, :focus)を使用する場合
  • メディアクエリを使用する場合
  • 複数のコンポーネントで同じスタイルを共有する場合
1
2
3
4
5
6
7
8
9
import './Button.css';

function Button({ variant }) {
  return (
    <button className={`btn btn-${variant}`}>
      クリック
    </button>
  );
}

classNameの動的な切り替え

条件に基づいてクラスを切り替える方法を解説します。

テンプレートリテラルを使用

1
2
3
4
5
6
7
function Button({ isActive, size }) {
  return (
    <button className={`btn ${isActive ? 'active' : ''} btn-${size}`}>
      ボタン
    </button>
  );
}

配列とjoinを使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function Card({ isHighlighted, isDisabled }) {
  const classes = ['card'];
  
  if (isHighlighted) {
    classes.push('highlighted');
  }
  if (isDisabled) {
    classes.push('disabled');
  }
  
  return <div className={classes.join(' ')}>カード</div>;
}

clsxライブラリの活用

複雑なクラス名の管理には、clsxライブラリが便利です。

1
npm install clsx
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import clsx from 'clsx';

function Button({ variant, size, isLoading, isDisabled }) {
  return (
    <button
      className={clsx(
        'btn',
        `btn-${variant}`,
        `btn-${size}`,
        {
          'btn-loading': isLoading,
          'btn-disabled': isDisabled
        }
      )}
      disabled={isDisabled}
    >
      ボタン
    </button>
  );
}

よくある間違いと対処法

JSXを書く際によく遭遇するエラーと解決方法を紹介します。

エラー1:隣接するJSX要素

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// エラー: Adjacent JSX elements must be wrapped in an enclosing tag
function BadComponent() {
  return (
    <h1>タイトル</h1>
    <p>本文</p>
  );
}

// 解決策: フラグメントで囲む
function GoodComponent() {
  return (
    <>
      <h1>タイトル</h1>
      <p>本文</p>
    </>
  );
}

エラー2:オブジェクトを直接表示

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// エラー: Objects are not valid as a React child
const user = { name: 'Taro', age: 25 };

function BadComponent() {
  return <p>{user}</p>;  // エラー
}

// 解決策: プロパティを個別に表示
function GoodComponent() {
  return <p>{user.name}{user.age}</p>;
}

エラー3:予約語の使用

1
2
3
4
5
6
7
8
9
// エラー: 'class' is a reserved word
function BadComponent() {
  return <div class="container">...</div>;
}

// 解決策: classNameを使用
function GoodComponent() {
  return <div className="container">...</div>;
}

エラー4:閉じタグの忘れ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// エラー: Expected corresponding JSX closing tag
function BadComponent() {
  return (
    <div>
      <input type="text">  // 閉じタグがない
    </div>
  );
}

// 解決策: 自己閉じタグを使用
function GoodComponent() {
  return (
    <div>
      <input type="text" />
    </div>
  );
}

エラー5:コメントの書き方

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// HTMLコメントは使えない
function BadComponent() {
  return (
    <div>
      <!-- これはエラー -->
      <p>テキスト</p>
    </div>
  );
}

// JSXでのコメント
function GoodComponent() {
  return (
    <div>
      {/* これがJSXのコメント */}
      <p>テキスト</p>
    </div>
  );
}

実践的なJSXパターン

実際の開発でよく使用されるJSXのパターンを紹介します。

条件付きレンダリングのパターン

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function Dashboard({ user, notifications, isLoading }) {
  // ローディング中
  if (isLoading) {
    return <div className="loading">読み込み中...</div>;
  }
  
  // 未ログイン
  if (!user) {
    return <div className="login-prompt">ログインしてください</div>;
  }
  
  // ログイン済み
  return (
    <div className="dashboard">
      <h1>ようこそ{user.name}さん</h1>
      {notifications.length > 0 && (
        <div className="notifications">
          <h2>通知{notifications.length}</h2>
          <ul>
            {notifications.map((n) => (
              <li key={n.id}>{n.message}</li>
            ))}
          </ul>
        </div>
      )}
    </div>
  );
}

スプレッド演算子でのProps転送

1
2
3
4
5
6
7
8
function Button(props) {
  return <button className="btn" {...props} />;
}

// 使用例
<Button onClick={handleClick} disabled={isDisabled}>
  送信
</Button>

まとめ

本記事では、ReactでUIを記述するためのJSX構文について解説しました。

  • JSXの基本:JavaScriptの中でHTMLライクにUIを記述する構文
  • 基本ルール:単一のルート要素、すべてのタグを閉じる、キャメルケースの属性名
  • 式の埋め込み{}を使用してJavaScript式を挿入
  • 条件分岐:三項演算子や&&演算子を活用
  • リストのレンダリング:map()メソッドとkey属性の組み合わせ
  • スタイルの適用:オブジェクト形式のインラインスタイルとclassNameの動的切り替え

JSXはReact開発の基礎となる重要な構文です。次の記事では、JSXを使ってUIの部品を作成する「コンポーネント」について詳しく解説します。

参考リンク