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

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​

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-size → fontSize)。

コメント​

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 で回避可能)
  • 閉じタグは必須
  • classはclassName、属性はキャメルケース
  • {}で JavaScript 式を埋め込める
備考

JSX に慣れないうちは、HTML との違いでエラーが出ることがあります。

次の章では、React の基本単位であるコンポーネントについて学びます。

次に読む​