はじめに#
前回の記事では、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>
);
}
|
注意点:countが0の場合、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-color → backgroundColor)
- 値は文字列または数値(数値の場合は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
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の部品を作成する「コンポーネント」について詳しく解説します。
参考リンク#