前回までで開発環境を整えました。
この記事では、実際にReactアプリを作成し、プロジェクトの構成を詳しく理解していきます。
各ファイルの役割を知ることで、今後の開発がスムーズになります。
JavaScript版とTypeScript版の両方記載しています。
学習したい方で実際にコードを動かしながら学習が進められます。
Contents
1. プロジェクトの作成
Reactの学習を始めるには、まず開発環境を整える必要があります。
ここでは、Viteという高速なビルドツールを使って、数分で動くReactアプリを作成します。
JavaScriptとTypeScriptの両方の手順を紹介しますので、自分に合った方を選んで進めてください。
Viteでプロジェクト作成
JavaScript版:
npm create vite@latest my-first-app -- --template react
cd my-first-app
npm install
npm run devTypeScript版:
npm create vite@latest my-first-app-ts -- --template react-ts
cd my-first-app-ts
npm install
npm run devnpm createで何度か入力を聞いてきますが、今回は、全て「エンターボタンを押す」で良いです。
ブラウザで http://localhost:5173 を開くと、Viteのデフォルト画面が表示されます。
💡 Point
“– –template react”はViteなどのビルドツールでプロジェクトを作成する際に使用するオプションです。
2. プロジェクト構成の全体像
プロジェクトを作成すると、たくさんのファイルやフォルダが自動生成されます。
最初は「これは何?」と戸惑うかもしれませんが、各ファイルには明確な役割があります。
全体像を把握することで、どこに何を書けば良いのかが分かるようになり、開発がスムーズに進みます。
JavaScript版の構成
my-first-app/
├── node_modules/ # インストールされたパッケージ(触らない)
├── public/ # 静的ファイル
│ └── vite.svg # ファビコン
├── src/ # ソースコード(メイン作業場所)
│ ├── assets/ # 画像などのアセット
│ │ └── react.svg
│ ├── App.css # Appコンポーネントのスタイル
│ ├── App.jsx # メインコンポーネント
│ ├── index.css # グローバルスタイル
│ └── main.jsx # エントリーポイント
├── .gitignore # Gitで無視するファイル
├── index.html # HTMLテンプレート
├── package.json # プロジェクト設定
├── package-lock.json # 依存関係のロックファイル
└── vite.config.js # Viteの設定ファイルTypeScript版の構成
my-first-app-ts/
├── node_modules/
├── public/
│ └── vite.svg
├── src/
│ ├── assets/
│ │ └── react.svg
│ ├── App.css
│ ├── App.tsx # .tsx拡張子
│ ├── index.css
│ └── main.tsx # .tsx拡張子
├── .gitignore
├── index.html
├── package.json
├── package-lock.json
├── tsconfig.json # TypeScript設定(参照用)
├── tsconfig.app.json # アプリ用TypeScript設定
├── tsconfig.node.json # Node/Vite用TypeScript設定
└── vite.config.ts # .ts拡張子以前のバージョンではsrc/vite-env.d.tsというファイルがありましたが、最新版ではtsconfig.app.jsonのtypes設定で型定義を指定する方式に変更されています。
"types": ["vite/client"],つまり、最新版のViteでは`vite-env.d.ts`ファイルは不要になり、代わりに`tsconfig.app.json`の`types`配列で型定義を指定する方式に変わったということですね。
3. 各ファイルの詳細解説
プロジェクトの構成が分かったら、次は個々のファイルの中身を理解しましょう。
index.html、main.jsx、App.jsxなど、それぞれのファイルがどんな役割を担っているのか、コードの意味は何なのかを一つずつ丁寧に解説します。
ここを理解すれば、Reactアプリの動作原理が見えてきます。
JavaScriptとTypeScriptの主な違い:
プロジェクトを作成する際、JavaScriptとTypeScriptのどちらかを選択しましたが、両者にはいくつかの違いがあります。
ファイル拡張子
- JavaScript版:
.js(通常のJS)、.jsx(JSXを含むファイル) - TypeScript版:
.ts(通常のTS)、.tsx(JSXを含むファイル)
設定ファイル
- JavaScript版:
vite.config.jsのみ - TypeScript版:
vite.config.ts、tsconfig.json、tsconfig.node.json、tsconfig.app.jsonなどが追加
型定義ファイル
TypeScript版のみ:src/vite-env.d.ts(Viteの型定義)
*現在は別の場所から参照しているので不要
コード内の違い
- TypeScript版では型注釈(
: string、: numberなど)が付く - 関数の戻り値の型(
: void、: JSX.Elementなど)を明示
これらの違いを意識しながら、以下で各ファイルを詳しく見ていきましょう。
index.html – HTMLテンプレート
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>my-first-app</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>重要なポイント:
<div id="root"></div>:
Reactアプリがマウントされる(作成したアプリの要素が埋め込まれるイメージ)場所<script type="module" src="/src/main.jsx"></script>:
エントリーポイントの読み込み- このファイルは通常ほとんど編集しません。
このページにアクセスする事でプログラムが実行されるキッカケ(エントリーポイントが読み込まれる)を作っていると考えてください。
main.jsx / main.tsx – エントリーポイント
JavaScript版(main.jsx):
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
)
TypeScript版(main.tsx):
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)
各部分の説明:
1. import文
import { StrictMode } from 'react' // React本体からStrictModeコンポーネントをインポート
import { createRoot } from 'react-dom/client' // DOM操作用 createRoot関数をインポート
import App from './App.tsx' // メインコンポーネント
import './index.css' // グローバルスタイル2. createRoot()
createRoot(document.getElementById('root'))index.htmlの<div id="root">を取得- React 18で導入された新しいAPI
- TypeScript版の「
!」は”非Null アサーション演算子”で「この要素は必ず存在する」という意味
3. render()
.render(
<StrictMode>
<App />
</StrictMode>,
)<App />コンポーネントをレンダリング(つまり、’root’要素の中にApp要素の内容が描画される)<StrictMode>は開発時に問題を検出するためのツール
💡 StrictModeとは?
- 開発モードでのみ動作(本番環境では影響なし)
- 非推奨のAPIを使っていないかチェック
- 副作用(useEffectなど)が2回実行される(意図的な動作)
- 初心者は「デバッグ用のモード」と理解すればOK
App.jsx / App.tsx – メインコンポーネント
デフォルトのコードを見てみましょう(簡略化):
JavaScript版(App.jsx):
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
function App() {
const [count, setCount] = useState(0)
return (
<>
<div>
<a href="https://vite.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.jsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
)
}
export default AppTypeScript版(App.tsx):
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
function App() {
const [count, setCount] = useState(0)
return (
<>
<div>
<a href="https://vite.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
)
}
export default App’function App() {’ か ’function App(): JSX.Element {’ どっち?
新しいバージョンでは、’function App() {’となっています。
型推論により、明示的に書かなくてもTypeScriptが自動的に型をチェックしているようです。
コードの解説:
1. useState – 状態管理
const [count, setCount] = useState(0)count: 現在の値setCount: 値を更新するための関数useState(0): 初期値は0
2. JSXの構造
<> {/* フラグメント */}
<div>...</div>
<h1>...</h1>
</>3. イベントハンドラ
<button onClick={() => setCount((count) => count + 1)}>- クリック時に
countを1増やす (count) => count + 1は関数形式の更新- setCountでcountを新しい値に更新
package.json – プロジェクト設定
{
"name": "my-first-app-ts",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"react": "^19.2.0",
"react-dom": "^19.2.0"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/node": "^24.10.1",
"@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.46.4",
"vite": "^7.2.4"
}
}JavaScript版は、typescript関連が無い
主要なスクリプト:
npm run dev: 開発サーバー起動npm run build: 本番用ビルドnpm run preview: ビルド結果のプレビュー
vite.config.js / vite.config.ts – Vite設定
JavaScript版(vite.config.js):
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
})TypeScript版(vite.config.ts):
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
})カスタマイズ例:
export default defineConfig({
plugins: [react()],
server: {
port: 3000, // ポート変更
open: true, // 自動でブラウザを開く
},
build: {
outDir: 'dist', // ビルド出力先
},
})tsconfig.app.json – TypeScript設定(TS版のみ)
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2022",
"useDefineForClassFields": true,
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"types": ["vite/client"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}重要な設定項目:
jsx: "react-jsx": JSXの変換方法strict: true: 厳格な型チェックnoUnusedLocals: true: 未使用変数を警告
初心者向けの緩い設定:
{
"compilerOptions": {
"strict": false,
"noUnusedLocals": false,
"noUnusedParameters": false
}
}4. 初めてのカスタマイズ
ファイルの役割が理解できたら、実際に手を動かしてコードを書き換えてみましょう。
デフォルトのコードをシンプルなカウンターアプリに変更することで、Reactの基本的な書き方や状態管理の仕組みを体感できます。
自分で書いたコードが動く喜びを味わってください。
シンプルなカウンターアプリ
JavaScript版(App.jsx):
import { useState } from 'react'
import './App.css'
function App() {
const [count, setCount] = useState(0)
const increment = () => {
setCount(count + 1)
}
const decrement = () => {
setCount(count - 1)
}
const reset = () => {
setCount(0)
}
return (
<div className="app">
<h1>カウンターアプリ</h1>
<div className="counter">
<p className="count-display">{count}</p>
<div className="button-group">
<button onClick={decrement}>-</button>
<button onClick={reset}>リセット</button>
<button onClick={increment}>+</button>
</div>
</div>
</div>
)
}
export default AppTypeScript版(App.tsx):
import { useState, type JSX } from 'react'
import './App.css'
function App(): JSX.Element {
const [count, setCount] = useState<number>(0)
const increment = (): void => {
setCount(count + 1)
}
const decrement = (): void => {
setCount(count - 1)
}
const reset = (): void => {
setCount(0)
}
return (
<div className="app">
<h1>カウンターアプリ</h1>
<div className="counter">
<p className="count-display">{count}</p>
<div className="button-group">
<button onClick={decrement}>-</button>
<button onClick={reset}>リセット</button>
<button onClick={increment}>+</button>
</div>
</div>
</div>
)
}
export default Appスタイル(App.css):
.app {
text-align: center;
padding: 2rem;
}
.counter {
margin: 2rem auto;
max-width: 400px;
padding: 2rem;
border: 2px solid #646cff;
border-radius: 8px;
background-color: #1a1a1a;
}
.count-display {
font-size: 4rem;
font-weight: bold;
margin: 1rem 0;
color: #646cff;
}
.button-group {
display: flex;
gap: 1rem;
justify-content: center;
}
button {
padding: 0.8rem 1.5rem;
font-size: 1.2rem;
border-radius: 8px;
border: 1px solid transparent;
background-color: #646cff;
color: white;
cursor: pointer;
transition: all 0.3s;
}
button:hover {
background-color: #535bf2;
transform: translateY(-2px);
}
button:active {
transform: translateY(0);
}実行と確認
npm run dev5. publicフォルダとassetsフォルダの違い
Reactプロジェクトには画像などのファイルを配置する場所が2つあります。
publicフォルダとsrc/assetsフォルダです。この2つは似ているようで全く異なる役割を持っています。
どちらに何を置くべきか理解することで、適切なファイル管理ができるようになります。
publicフォルダ
公開ファイルの保存先です。直接アクセスさせたいファイルはこちらに保存する
public/
└── vite.svg特徴:
- ビルド時にそのままビルド先へコピーされる
- URLで直接アクセス可能
- ファイル名がハッシュ化されない(ブラウザーのキャッシュによって最新画像に変わらない場合がある)
使用例:
<!-- index.htmlから参照 -->
<link rel="icon" type="image/svg+xml" href="/vite.svg" />適している用途:
- ファビコン
- robots.txt
- manifest.json
- 固定のアセットファイル
src/assetsフォルダ
src/assets/
└── react.svg特徴:
- Viteのビルドシステムで処理される
- import文で読み込む
- ファイル名がハッシュ化される(キャッシュ対策)
- 最適化される
使用例:
import logo from './assets/react.svg'
function App() {
return <img src={logo} alt="Logo" />
}適している用途:
- コンポーネント内で使う画像
- CSSで使う画像
- 動的に読み込む画像
6. Hot Module Replacement (HMR)
Viteを使った開発で最も便利な機能の一つが「HMR(ホットモジュールリプレースメント)」です。
コードを保存するだけで、ブラウザが自動的に更新され、しかもアプリの状態(例えばカウンターの数値)は保持されたままです。
この機能により、開発効率が劇的に向上します。
HMRとは?
- コードを保存すると、ページ全体をリロードせずに変更が反映される
- アプリの状態を保ったまま更新できる
- 開発効率が大幅に向上
試してみよう:
npm run devでサーバーを起動App.jsxの内容を編集- 保存すると即座にブラウザに反映される
- カウンターの値もリセットされない(状態が保持される)
7. ビルドと本番環境
開発中はnpm run devで開発サーバーを起動していますが、実際にWebサイトを公開する際には「ビルド」という作業が必要です。
ビルドすることで、コードが最適化され、読み込みが高速になります。
ここでは開発環境と本番環境の違いと、ビルドの方法について学びます。
開発用ビルド
npm run dev- ソースマップ付き
- 詳細なエラーメッセージ
- HMR有効
- ファイルサイズは大きい
本番用ビルド
npm run build- コードが圧縮(minify)される
- 不要なコードが削除される
- ファイルサイズが最適化される
distフォルダに出力される
ビルド結果の確認:
npm run preview本番環境と同じ状態でローカルで確認できます。
distフォルダの構造
dist/
├── assets/
│ ├── index-abc123.css # ハッシュ付きファイル名
│ └── index-def456.js
├── vite.svg
└── index.htmlこのフォルダをサーバーにデプロイすれば公開できます。
8. よくある初心者の疑問
Reactを学び始めると、「なぜこう書くの?」「これは必要なの?」といった疑問が次々と出てきます。
ここでは、初心者がつまずきやすいポイントや、よく聞かれる質問について分かりやすく解説します。
理解が深まれば、自信を持ってコードを書けるようになります。
Q1: なぜ.jsxや.tsxを使うの?
.js/.ts: 通常のJavaScript/TypeScript.jsx/.tsx: JSXを含むファイル
Viteでは.jsx/.tsxを使わないとエラーになります。
Q2: import ‘./App.css’の意味は?
- Viteが自動的に
<style>タグとして埋め込む - モジュール化されたCSSとして扱える
Q3: なぜexport defaultを使うの?
// App.jsx
function App() { ... }
export default App // これで他のファイルから使える
// main.jsx
import App from './App.jsx' // インポートできるQ4: React.StrictModeは必要?
- 本番環境では影響しない
- 初心者は気にしなくてOK
- 外しても問題なし
9. 実践:Todoアプリの基礎
ここまで学んだ知識を活かして、実際に動くアプリを作ってみましょう。
シンプルなTodoアプリの骨組みを作成することで、useState、イベント処理、リストのレンダリングなど、Reactの基本的な概念を実践的に理解できます。
まずは動くものを作ることが、学習の最良の方法です。
JavaScript版(App.jsx):
import { useState } from 'react'
import './App.css'
function App() {
const [todos, setTodos] = useState([])
const [inputValue, setInputValue] = useState('')
const addTodo = () => {
if (inputValue.trim()) {
setTodos([...todos, inputValue])
setInputValue('')
}
}
return (
<div className="app">
<h1>シンプルTodo</h1>
<div className="input-area">
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="やることを入力"
/>
<button onClick={addTodo}>追加</button>
</div>
<ul className="todo-list">
{todos.map((todo, index) => (
<li key={index}>{todo}</li>
))}
</ul>
</div>
)
}
export default AppTypeScript版(App.tsx):
import { useState, type JSX } from 'react'
import './App.css'
function App(): JSX.Element {
const [todos, setTodos] = useState<string[]>([])
const [inputValue, setInputValue] = useState<string>('')
const addTodo = (): void => {
if (inputValue.trim()) {
setTodos([...todos, inputValue])
setInputValue('')
}
}
return (
<div className="app">
<h1>シンプルTodo</h1>
<div className="input-area">
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="やることを入力"
/>
<button onClick={addTodo}>追加</button>
</div>
<ul className="todo-list">
{todos.map((todo, index) => (
<li key={index}>{todo}</li>
))}
</ul>
</div>
)
}
export default Appまとめ
この記事では、Reactプロジェクトの構成を詳しく学びました。
学んだこと:
- プロジェクトのディレクトリ構造
- 各ファイルの役割(main.jsx、App.jsx、index.htmlなど)
- JavaScriptとTypeScriptの違い
- publicとassetsの使い分け
- HMR(ホットモジュールリプレースメント)
- ビルドと本番環境への準備
重要なポイント:
main.jsxがエントリーポイントApp.jsxがメインコンポーネントindex.htmlのrootにReactアプリがマウントされる- HMRで効率的に開発できる
次のステップ: 次回は、JSXの詳細な書き方とルールについて深く学びます。条件分岐、リスト表示、イベント処理など、実践的なJSXの使い方をマスターしましょう!
プロジェクト構成を理解することで、ファイルを探したり編集したりする作業がスムーズになります。最初は覚えることが多いですが、何度も触っているうちに自然と身につきますよ!


























