メインコンテンツまでスキップ

JSXの基本

この章では、ReactのUIを記述するための構文であるJSXを学びます。

この章で学ぶこと

  • JSXとは何か、なぜ使うのか
  • JSXの4つの基本ルール
  • JavaScriptの埋め込み方法
  • インラインスタイルの適用方法
備考

この章では03章で作成したプロジェクトを使用します。npm run devで開発サーバーを起動し、コードを書きながら学習を進めましょう。

Viteテンプレートのリセット

Viteのデフォルトテンプレートには、デモ用のスタイルが含まれています。これから自分でコードを書いていくため、シンプルな状態にリセットしましょう。

src/index.cssを以下の内容に置き換えてください。

:root {
font-family: system-ui, -apple-system, sans-serif;
line-height: 1.5;
}

body {
margin: 0;
padding: 20px;
}

button {
padding: 8px 16px;
font-size: 1rem;
cursor: pointer;
}

nav a {
margin-right: 16px;
}

src/App.cssを以下の内容に置き換えてください。

#root {
max-width: 800px;
margin: 0 auto;
text-align: center;
}

これでシンプルな状態になりました。テキストは中央寄せで表示され、ボタンの基本スタイルも用意されています。

備考

本文中のコード例では、スタイルのインポート(import "./App.css")を省略している場合があります。演習の回答コードは完全な形で記載しているため、そのままApp.tsxにコピーして動作確認できます。

最初のJSX

src/App.tsxを開いてみましょう。Viteのテンプレートには最初から多くのコードがありますが、これをシンプルにしてJSXの基本を学びます。

src/App.tsxを以下のように書き換えてください。

function App() {
return <h1>Hello, React!</h1>
}

export default App

ブラウザを確認すると「Hello, React!」と表示されます。この<h1>Hello, React!</h1>がJSXです。

JSXとは

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

const element = <h1>Hello, React!</h1>

JSXはブラウザが直接理解できないため、ビルド時にJavaScriptに変換されます。

JSXの内部変換

JSXはビルド時にreact/jsx-runtimeが提供するjsx()関数の呼び出しに変換されます(automatic runtime。React 17で導入され、Viteでも標準の方式です)。

// JSXで記述
const element = <h1 className="title">Hello</h1>

// 変換後のJavaScript(イメージ。importはビルド時に自動挿入される)
import { jsx as _jsx } from 'react/jsx-runtime'

const element = _jsx('h1', {
className: 'title',
children: 'Hello'
})

なお、従来の方式(classic transform)ではReact.createElement('h1', { className: 'title' }, 'Hello')に変換されていました。解説記事では今もこの形を見かけますが、どちらの方式でも「JSX 1つ = 関数呼び出し1つ」である点は同じです。この変換を理解すると、JSXの制約(単一ルート要素が必要など)の理由がわかります。

JSXの基本ルール

1. 単一のルート要素

JSXは必ず単一のルート要素を返す必要があります。

// OK: 単一のルート要素
const element = (
<div>
<h1>タイトル</h1>
<p>本文</p>
</div>
)

// NG: 複数のルート要素
const element = (
<h1>タイトル</h1>
<p>本文</p>
)

Fragment

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

import { Fragment } from 'react'

const element = (
<Fragment>
<h1>タイトル</h1>
<p>本文</p>
</Fragment>
)

// 省略記法
const element = (
<>
<h1>タイトル</h1>
<p>本文</p>
</>
)

2. 閉じタグは必須

HTMLでは省略できる閉じタグも、JSXでは必須です。

// OK
<img src="image.png" alt="画像" />
<br />
<input type="text" />

// NG
<img src="image.png" alt="画像">

3. className属性

HTMLのclass属性は、JSXではclassNameを使用します。

<div className="container">
<p className="text-primary">テキスト</p>
</div>
備考

classではなくclassNameを使う理由は、classがJavaScriptの予約語だからです。JSXはJavaScript内で動作するため、予約語との衝突を避ける必要があります。

4. キャメルケース

JSXの属性名はキャメルケースで記述します。

// HTMLの属性 → JSXの属性
// onclick → onClick
// tabindex → tabIndex
// for → htmlFor

<button onClick={handleClick}>クリック</button>
<label htmlFor="email">メール</label>

JavaScriptの埋め込み

JSX内では、波括弧{}を使ってJavaScript式を埋め込めます。※テンプレートリテラルや式については02章で解説しています。

変数の表示

const name = 'React'
const element = <h1>Hello, {name}!</h1>

式の評価

const element = <p>1 + 1 = {1 + 1}</p>

関数の呼び出し

const formatDate = (date: Date) => date.toLocaleDateString()
const element = <p>今日: {formatDate(new Date())}</p>

オブジェクトのプロパティ

const user = { name: '田中', age: 25 }
const element = <p>{user.name}さん({user.age}歳)</p>

スタイルの適用

インラインスタイル

インラインスタイルはオブジェクトで指定します。

const style = {
color: 'blue',
fontSize: '20px',
backgroundColor: '#f0f0f0'
}

const element = <p style={style}>スタイル付きテキスト</p>

// 直接記述する場合
const element = <p style={{ color: 'red', fontWeight: 'bold' }}>テキスト</p>
警告

インラインスタイルではプロパティ名がキャメルケースになります(font-sizefontSize)。

コメント

JSX内でコメントを書く場合は、波括弧で囲みます。

const element = (
<div>
{/* これはコメントです */}
<p>テキスト</p>
</div>
)

試してみよう

以下の課題に挑戦してみましょう。

  1. classNameを試す: <h1>className="title"を追加し、どう表示されるか確認してみましょう
  2. classを使ってみる: classNameの代わりにclassを使うとどうなるか試してみましょう(警告を確認)
  3. JavaScriptを埋め込む: 変数nameを定義し、{name}で表示してみましょう
  4. Fragmentを試す: <></>で複数の要素を囲んでみましょう
ヒント
  1. className="title"を追加するだけでOKです。CSSが定義されていなくても属性は適用されます。ブラウザの開発者ツールでHTML要素を確認してみましょう。

  2. ブラウザのコンソール(F12キー → Console)を開いてから、class属性を使ったコードを保存してください。警告メッセージが表示されます。なお、エディタにはTypeScriptの型エラー(赤線)が表示されますが、開発サーバーはそのまま実行できます。

  3. 関数内でconst name = 'React'のように変数を定義し、JSX内で{name}と記述すると変数の値が表示されます。

  4. 複数の要素を返したいときは<></>で囲みます。例えば<h1><p>を並べて返すことができます。

回答と解説

課題1: classNameを試す

import './App.css'

function App() {
return <h1 className="title">Hello, React!</h1>
}

export default App

className属性を追加すると、HTMLではclass="title"として出力されます。ブラウザの開発者ツール(F12)でElements/要素タブを開くと、<h1 class="title">となっていることが確認できます。CSSで.titleセレクタにスタイルを定義すれば、見た目が変わります。


課題2: classを使ってみる

import './App.css'

function App() {
return <h1 class="title">Hello, React!</h1>
}

export default App

ブラウザのコンソールに以下のような警告が表示されます。

Invalid DOM property `class`. Did you mean `className`?

classはJavaScriptの予約語(クラス定義に使う)のため、JSXではclassNameを使う必要があります。React 16以降、誤った属性名も警告付きで文字列化されそのままDOMに渡されるためclass="title"自体は描画されますが、開発時に上記の警告がコンソールに表示されます。React公式の推奨は常にclassNameなので、新規コードではclassNameを使ってください。また、TypeScriptプロジェクトではclass属性は型エラー(Did you mean 'className'?)としてエディタ上でも検出されます。実行前に間違いに気づけるのはTypeScriptを使う利点の1つです。確認が終わったらclassNameに戻しておきましょう。


課題3: JavaScriptを埋め込む

import './App.css'

function App() {
const name = 'React'

return <h1>Hello, {name}!</h1>
}

export default App

波括弧{}内にJavaScriptの式を書くと、その評価結果が表示されます。変数だけでなく、{1 + 1}のような計算式や、{name.toUpperCase()}のようなメソッド呼び出しも可能です。


課題4: Fragmentを試す

import './App.css'

function App() {
return (
<>
<h1>タイトル</h1>
<p>本文です</p>
</>
)
}

export default App

<></>(Fragment)を使うと、余分な<div>を追加せずに複数の要素を返せます。HTMLの構造をシンプルに保ちたいときに使えます。開発者ツールで確認すると、<h1><p>が直接<div id="root">の中に配置されていることがわかります。

まとめ

  • JSXはJavaScriptの中でHTMLライクな構文を書ける
  • 単一のルート要素が必要(Fragmentで回避可能)
  • 閉じタグは必須
  • classclassName、属性はキャメルケース
  • {}でJavaScript式を埋め込める
備考

JSXに慣れないうちは、HTMLとの違いでエラーが出ることがあります。特にclassNameの使用、閉じタグの必須化、キャメルケースの属性名を意識しましょう。

次の章では、Reactの核となるコンポーネントについて学びます。

次に読む