T-CREATOR

Vite で React プロジェクトを立ち上げる方法

Vite で React プロジェクトを立ち上げる方法

React 開発において、適切な開発環境の選択は成功の鍵を握ります。近年、多くの React 開発者が Create React App から新しいビルドツールへと移行を検討しており、その中でも Vite は圧倒的な支持を集めています。

Vite を使った React 開発は、従来の開発体験を根本から変える革新的なアプローチです。瞬時の起動、高速なホットリロード、そして直感的な設定により、開発者は本来のアプリケーション開発に集中できるようになります。特に大規模な React アプリケーション開発では、その差は歴然としており、開発効率の向上は数倍にも及びます。

この記事では、Vite で React プロジェクトを立ち上げる具体的な方法を、実践的な観点から詳しく解説いたします。基本的なプロジェクト作成から、TypeScript 統合、状態管理、UI ライブラリの連携まで、実際の開発で必要となる全ての手順を段階的にご紹介します。Yarn を活用した効率的な依存関係管理と、React 開発者が知っておくべき最適化テクニックを習得していただけるでしょう。

背景

React 開発における Vite の優位性

React 開発における Vite の最大の優位性は、開発者体験(DX)の劇的な改善にあります。従来のビルドツールでは実現できなかった高速性と柔軟性を兼ね備えており、React 開発において新たな標準となりつつあります。

起動速度の革命的改善

Vite の React プロジェクトは、プロジェクトサイズに関係なく数秒での起動を実現します:

bash# Vite + React(即座に起動)
yarn dev
# → 開発サーバーが1〜3秒で起動完了

# Create React App(プロジェクト規模に比例して遅延)
yarn start
# → 大規模プロジェクトでは30秒〜数分の待機時間

この速度差は、日々の開発作業において累積的に大きな影響を与えます。一日に何度も開発サーバーを再起動する React 開発において、この時間短縮は開発者の集中力維持と生産性向上に直結するのです。

JSX/TSX に最適化されたホットリロード

Vite の Hot Module Replacement(HMR)は、React 特有のコンポーネント開発に特化した設計となっています:

typescript// Reactコンポーネントの変更が瞬時に反映
function MyComponent() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>カウント: {count}</h1>
      <button onClick={() => setCount(count + 1)}>
        増加
      </button>
    </div>
  );
}

コンポーネントの変更が保存と同時に画面に反映され、アプリケーションの状態も保持されます。これにより、複雑な UI の開発やデバッグ作業が格段に効率化されるのです。

React 開発者向け最適化機能

Vite は React 開発に特化した多くの最適化を提供します:

| 機能 | Vite での対応 | 開発への影響 | | ---- | ------------------ | -------------------------- | ---------------- | | # 1 | JSX 自動変換 | import なしで JSX 使用可能 | コード簡素化 | | # 2 | React Fast Refresh | コンポーネント状態保持 HMR | デバッグ効率向上 | | # 3 | TypeScript 統合 | 設定不要の TS 対応 | 型安全性確保 | | # 4 | CSS Modules 対応 | スコープ化 CSS 自動処理 | スタイル競合回避 |

従来の Create React App からの移行需要

Create React App(CRA)は長年 React 開発の標準ツールでしたが、現代の開発要件に対していくつかの制約が顕在化してきました。

パフォーマンス制約の顕在化

大規模 React アプリケーションにおいて、CRA のパフォーマンス制約は深刻な問題となっています:

開発時のボトルネック

  • 初回起動時間:中規模プロジェクトで 30 秒〜1 分
  • ホットリロード遅延:変更反映に 3〜10 秒
  • ビルド時間:本番ビルドに 5〜15 分

実際の開発現場での影響

  • 開発者の待機時間増加による集中力低下
  • デバッグサイクルの長期化
  • チーム全体の開発効率低下

設定カスタマイズの困難さ

CRA の「設定なし」アプローチは、初心者には優しい一方で、本格的な開発では制約となります:

bash# CRAでカスタマイズが必要な場合
yarn eject
# → 元に戻せない不可逆的変更
# → 設定ファイルが大量に露出
# → メンテナンス負荷の増大

Vite ではこのような All or Nothing アプローチではなく、必要な部分のみを段階的にカスタマイズできます。

モダン開発ツールとの統合課題

近年の React 開発では、多様なツールとの統合が必要となっていますが、CRA ではその統合が困難な場合があります:

  • Tailwind CSS: CRA では追加設定が複雑
  • Storybook: 設定の重複と競合
  • Vitest: テスト環境の統合困難
  • Micro-frontends: アーキテクチャ対応不足

モダン React 開発でのツール選択基準

現代の React 開発では、ツール選択において以下の基準が重要となっています。

開発者体験(DX)重視

高速フィードバックループ

  • 変更から結果確認までの時間最小化
  • エラー発生時の迅速な原因特定
  • デバッグ作業の効率化

直感的な設定管理

  • 段階的なカスタマイズ対応
  • 設定の可読性と保守性
  • チーム間での設定共有容易性

プロダクション品質の確保

現代の React アプリケーションには、高い品質基準が求められます:

typescript// パフォーマンス最適化例
const LazyComponent = lazy(
  () => import('./HeavyComponent')
);

function App() {
  return (
    <Router>
      <Suspense fallback={<Loading />}>
        <Routes>
          <Route
            path='/heavy'
            element={<LazyComponent />}
          />
        </Routes>
      </Suspense>
    </Router>
  );
}

品質管理要件

  • Code Splitting による最適化
  • Tree Shaking による不要コード除去
  • TypeScript による型安全性
  • 自動テスト環境の統合

課題

Create React App 環境での開発速度限界

Create React App で開発を続ける中で、多くのチームが直面する根本的な速度限界があります。

Webpack 4 ベースアーキテクチャの制約

CRA の内部で使用されている Webpack 4 は、現代の React 開発要件に対して構造的な制約を抱えています:

バンドル処理の非効率性

  • 全ファイルの事前バンドル必要
  • 依存関係の線形処理
  • キャッシュ機構の限界

実際の開発現場では、以下のような問題が顕在化します:

bash# 大規模Reactプロジェクトでの典型的な待機時間
起動時間: 45秒〜2分
ホットリロード: 5〜15秒
フルビルド: 10〜30分

スケーラビリティの欠如

プロジェクトの成長に伴い、CRA の性能劣化は加速度的に進行します:

プロジェクト規模コンポーネント数CRA 起動時間Vite 起動時間
# 1小規模(50 個未満)10 秒2 秒
# 2中規模(100-300 個)45 秒3 秒
# 3大規模(500 個以上)2 分以上5 秒

設定カスタマイズの困難さ

React 開発における実際のニーズに対して、CRA の設定システムは柔軟性に欠けています。

All-or-Nothing 問題

CRA でカスタマイズを行う場合、ejectという不可逆的な操作が必要となります:

bash# eject実行後の影響
yarn eject
# ↓
# 60+の設定ファイルが露出
# webpack.config.js: 500+行の複雑設定
# package.json: 50+の依存関係追加
# → 元の状態に戻すことは不可能

段階的カスタマイズの不可能性

実際の React 開発では、プロジェクトの成長に合わせて段階的な設定調整が必要です:

初期段階のニーズ

  • TypeScript 統合
  • Absolute imports 設定
  • CSS Preprocessor 追加

成長段階のニーズ

  • Bundle 分析と最適化
  • 環境別設定管理
  • Advanced TypeScript 設定

CRA ではこれらを段階的に導入することが困難で、初期から複雑な設定を強いられてしまいます。

React 特有の開発環境要件

React 開発には、フレームワーク特有の要件があり、これらに対する最適化が重要です。

コンポーネント開発の特殊性

React のコンポーネントベース開発には、特有の開発フローがあります:

typescript// 典型的なReactコンポーネント開発フロー
function ProductCard({ product, onAddToCart }) {
  const [isLoading, setIsLoading] = useState(false);
  const [quantity, setQuantity] = useState(1);

  const handleAddToCart = async () => {
    setIsLoading(true);
    try {
      await onAddToCart(product.id, quantity);
      // 成功時のUI更新を即座に確認したい
    } catch (error) {
      // エラー状態のUI確認が必要
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <div
      className={`product-card ${
        isLoading ? 'loading' : ''
      }`}
    >
      {/* UIの変更を瞬時に確認したい */}
    </div>
  );
}

React 開発特有の要件

  • 状態変更の即座な視覚確認
  • プロップの変更に対するリアクティブ更新
  • Hooks の動作検証とデバッグ

状態管理統合の複雑性

モダン React 開発では、状態管理ライブラリとの深い統合が必要です:

typescript// Redux Toolkit + RTK Query統合例
const store = configureStore({
  reducer: {
    auth: authSlice.reducer,
    products: productsSlice.reducer,
    cart: cartSlice.reducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(apiSlice.middleware),
});

CRA ではこのような複雑な統合設定が困難で、開発効率を大幅に低下させることがあります。

解決策

Vite による React 開発環境の革新

Vite は React 開発において、従来の制約を根本的に解決する革新的なアプローチを提供します。

ESModules ベースの高速アーキテクチャ

Vite の ESModules ベースアーキテクチャは、React 開発に最適化された設計となっています:

typescript// vite.config.ts - React最適化設定
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [
    react({
      // React Fast Refresh有効化
      fastRefresh: true,
      // JSX自動変換
      jsxImportSource: '@emotion/react',
      // 開発時のランタイム最適化
      babel: {
        plugins: [
          [
            '@emotion/babel-plugin',
            { autoLabel: 'dev-only' },
          ],
        ],
      },
    }),
  ],

  // React開発向けサーバー設定
  server: {
    port: 3000,
    open: true,
    hmr: {
      overlay: true, // エラーオーバーレイ表示
    },
  },

  // React本番ビルド最適化
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          react: ['react', 'react-dom'],
          vendor: ['lodash', 'axios'],
        },
      },
    },
  },
});

段階的設定カスタマイズシステム

Vite では、プロジェクトの成長に合わせて段階的に設定を拡張できます:

フェーズ 1: 基本設定

typescript// 最小限の設定でスタート
export default defineConfig({
  plugins: [react()],
});

フェーズ 2: 開発効率化

typescript// パス解決とプリプロセッサ追加
export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src'),
      '@components': resolve(__dirname, 'src/components'),
    },
  },
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@import "@/styles/variables.scss";`,
      },
    },
  },
});

Hot Module Replacement の最適化

Vite の HMR は、React 開発において特別に最適化されており、従来のツールでは実現できない開発体験を提供します。

React Fast Refresh との深い統合

React Fast Refresh は Vite と深く統合されており、コンポーネントの状態を保持しながらコードの変更を反映します:

typescript// コンポーネント状態が保持される例
function Counter() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('');

  return (
    <div>
      <h1>カウント: {count}</h1>
      <input
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder='名前を入力'
      />
      <button onClick={() => setCount(count + 1)}>
        増加
      </button>
      {/* このコンポーネントのUIを変更しても、
          countとnameの状態は保持される */}
    </div>
  );
}

エラーバウンダリとの統合

開発時のエラーハンドリングも最適化されています:

typescript// 開発時エラー情報の詳細表示
if (import.meta.env.DEV) {
  window.addEventListener('unhandledrejection', (event) => {
    console.error('未処理のPromise拒否:', event.reason);
    // Viteが自動的にエラーオーバーレイを表示
  });
}

React 開発者向け特化機能

Vite は React 開発者のニーズに特化した多くの機能を提供します。

JSX/TSX 最適化処理

JSX の処理パフォーマンスが大幅に向上しています:

typescript// 大量のJSX要素も高速処理
function ProductGrid({ products }) {
  return (
    <div className='grid grid-cols-4 gap-4'>
      {products.map((product) => (
        <ProductCard
          key={product.id}
          product={product}
          onAddToCart={handleAddToCart}
          // 複雑なpropsも瞬時に反映
        />
      ))}
    </div>
  );
}

TypeScript 統合の簡素化

TypeScript との統合が大幅に簡素化されています:

typescript// tsconfig.json - Vite最適化設定
{
  "compilerOptions": {
    "target": "ESNext",
    "lib": ["DOM", "DOM.Iterable", "ES6"],
    "allowJs": false,
    "skipLibCheck": true,
    "esModuleInterop": false,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "ESNext",
    "moduleResolution": "Node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": ["src"]
}

具体例

基本的な React プロジェクト作成手順

実際に Vite を使って React プロジェクトを作成する詳細な手順を示します。

プロジェクト初期化

bash# 1. Vite + Reactプロジェクト作成
yarn create vite my-react-app --template react

# 2. プロジェクトディレクトリに移動
cd my-react-app

# 3. 依存関係インストール
yarn install

# 4. 開発サーバー起動(瞬時に起動)
yarn dev

基本的なプロジェクト構造

作成されるプロジェクト構造を確認し、React 開発に必要な要素を把握します:

arduinomy-react-app/
├── public/
│   └── vite.svg
├── src/
│   ├── assets/
│   │   └── react.svg
│   ├── App.css
│   ├── App.jsx
│   ├── index.css
│   └── main.jsx
├── index.html
├── package.json
└── vite.config.js

基本コンポーネントの動作確認

typescript// src/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://vitejs.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>
    </>
  );
}

export default App;

TypeScript + React 環境の構築

TypeScript と React の統合環境を構築します。

TypeScript 環境移行

bash# 1. TypeScript関連パッケージ追加
yarn add -D typescript @types/react @types/react-dom

# 2. ファイル拡張子変更
mv src/main.jsx src/main.tsx
mv src/App.jsx src/App.tsx

# 3. TypeScript設定ファイル作成
cat > tsconfig.json << EOF
{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["src"],
  "references": [{ "path": "./tsconfig.node.json" }]
}
EOF

TypeScript 化された React コンポーネント

typescript// src/App.tsx - TypeScript版
import { useState } from 'react';
import reactLogo from './assets/react.svg';
import viteLogo from '/vite.svg';
import './App.css';

// Props型定義例
interface CounterProps {
  initialCount?: number;
  onCountChange?: (count: number) => void;
}

function Counter({
  initialCount = 0,
  onCountChange,
}: CounterProps) {
  const [count, setCount] = useState<number>(initialCount);

  const handleIncrement = (): void => {
    const newCount = count + 1;
    setCount(newCount);
    onCountChange?.(newCount);
  };

  return (
    <button onClick={handleIncrement}>
      count is {count}
    </button>
  );
}

function App(): JSX.Element {
  const [totalClicks, setTotalClicks] = useState<number>(0);

  const handleCountChange = (count: number): void => {
    setTotalClicks(count);
  };

  return (
    <>
      <div>
        <a href='https://vitejs.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 + TypeScript</h1>
      <div className='card'>
        <Counter onCountChange={handleCountChange} />
        <p>総クリック数: {totalClicks}</p>
      </div>
    </>
  );
}

export default App;

React Router 導入と設定

SPA ルーティングを実現する React Router の統合を行います。

React Router セットアップ

bash# React Router インストール
yarn add react-router-dom
yarn add -D @types/react-router-dom

ルーティング設定実装

typescript// src/main.tsx - Router統合
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App.tsx';
import './index.css';

ReactDOM.createRoot(
  document.getElementById('root')!
).render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
);
typescript// src/App.tsx - ルート定義
import {
  Routes,
  Route,
  Link,
  useLocation,
} from 'react-router-dom';
import { useState } from 'react';
import './App.css';

// ページコンポーネント定義
function Home() {
  return (
    <div>
      <h2>ホームページ</h2>
      <p>
        Vite + React + TypeScript
        のデモアプリケーションです。
      </p>
    </div>
  );
}

function About() {
  return (
    <div>
      <h2>概要</h2>
      <p>
        このアプリケーションはViteの高速開発環境を体験するためのサンプルです。
      </p>
    </div>
  );
}

function Counter() {
  const [count, setCount] = useState<number>(0);

  return (
    <div>
      <h2>カウンター</h2>
      <button onClick={() => setCount(count + 1)}>
        カウント: {count}
      </button>
    </div>
  );
}

// ナビゲーションコンポーネント
function Navigation() {
  const location = useLocation();

  const navStyle = {
    padding: '20px',
    borderBottom: '1px solid #ccc',
    marginBottom: '20px',
  };

  const linkStyle = (path: string) => ({
    marginRight: '20px',
    color: location.pathname === path ? '#646cff' : '#333',
    textDecoration: 'none',
    fontWeight:
      location.pathname === path ? 'bold' : 'normal',
  });

  return (
    <nav style={navStyle}>
      <Link to='/' style={linkStyle('/')}>
        ホーム
      </Link>
      <Link to='/about' style={linkStyle('/about')}>
        概要
      </Link>
      <Link to='/counter' style={linkStyle('/counter')}>
        カウンター
      </Link>
    </nav>
  );
}

function App(): JSX.Element {
  return (
    <div className='App'>
      <h1>Vite + React Router デモ</h1>
      <Navigation />

      <Routes>
        <Route path='/' element={<Home />} />
        <Route path='/about' element={<About />} />
        <Route path='/counter' element={<Counter />} />
      </Routes>
    </div>
  );
}

export default App;

状態管理(Redux Toolkit)統合

大規模アプリケーション開発に必要な状態管理システムを統合します。

Redux Toolkit セットアップ

bash# Redux Toolkit インストール
yarn add @reduxjs/toolkit react-redux
yarn add -D @types/react-redux

ストア設定とスライス定義

typescript// src/store/index.ts - ストア設定
import { configureStore } from '@reduxjs/toolkit';
import counterSlice from './slices/counterSlice';
import userSlice from './slices/userSlice';

export const store = configureStore({
  reducer: {
    counter: counterSlice,
    user: userSlice,
  },
  devTools: import.meta.env.DEV, // 開発環境でのみDevTools有効
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
typescript// src/store/slices/counterSlice.ts - カウンタースライス
import {
  createSlice,
  PayloadAction,
} from '@reduxjs/toolkit';

interface CounterState {
  value: number;
  step: number;
}

const initialState: CounterState = {
  value: 0,
  step: 1,
};

export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) => {
      state.value += state.step;
    },
    decrement: (state) => {
      state.value -= state.step;
    },
    incrementByAmount: (
      state,
      action: PayloadAction<number>
    ) => {
      state.value += action.payload;
    },
    setStep: (state, action: PayloadAction<number>) => {
      state.step = action.payload;
    },
    reset: (state) => {
      state.value = 0;
    },
  },
});

export const {
  increment,
  decrement,
  incrementByAmount,
  setStep,
  reset,
} = counterSlice.actions;
export default counterSlice.reducer;

React-Redux 統合コンポーネント

typescript// src/hooks/redux.ts - 型安全なHooks
import {
  useDispatch,
  useSelector,
  TypedUseSelectorHook,
} from 'react-redux';
import type { RootState, AppDispatch } from '../store';

export const useAppDispatch = () =>
  useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> =
  useSelector;
typescript// src/components/ReduxCounter.tsx - Redux統合コンポーネント
import {
  useAppSelector,
  useAppDispatch,
} from '../hooks/redux';
import {
  increment,
  decrement,
  incrementByAmount,
  setStep,
  reset,
} from '../store/slices/counterSlice';

function ReduxCounter(): JSX.Element {
  const count = useAppSelector(
    (state) => state.counter.value
  );
  const step = useAppSelector(
    (state) => state.counter.step
  );
  const dispatch = useAppDispatch();

  return (
    <div
      style={{
        padding: '20px',
        border: '1px solid #ccc',
        margin: '20px',
      }}
    >
      <h3>Redux Counter</h3>
      <p>
        現在の値: <strong>{count}</strong>
      </p>
      <p>ステップ: {step}</p>

      <div style={{ marginBottom: '10px' }}>
        <button onClick={() => dispatch(increment())}>
          + {step}
        </button>
        <button
          onClick={() => dispatch(decrement())}
          style={{ marginLeft: '10px' }}
        >
          - {step}
        </button>
        <button
          onClick={() => dispatch(reset())}
          style={{ marginLeft: '10px' }}
        >
          リセット
        </button>
      </div>

      <div style={{ marginBottom: '10px' }}>
        <label>
          ステップサイズ:
          <input
            type='number'
            value={step}
            onChange={(e) =>
              dispatch(setStep(Number(e.target.value)))
            }
            style={{ marginLeft: '10px', width: '60px' }}
          />
        </label>
      </div>

      <div>
        <button
          onClick={() => dispatch(incrementByAmount(10))}
        >
          +10
        </button>
        <button
          onClick={() => dispatch(incrementByAmount(-10))}
          style={{ marginLeft: '10px' }}
        >
          -10
        </button>
      </div>
    </div>
  );
}

export default ReduxCounter;

UI ライブラリ(MUI/Chakra UI)連携

プロフェッショナルな UI を効率的に構築するための UI ライブラリ統合を行います。

Material-UI (MUI) 統合

bash# MUI インストール
yarn add @mui/material @emotion/react @emotion/styled
yarn add @mui/icons-material
typescript// src/components/MuiDemo.tsx - MUI統合例
import {
  Box,
  Button,
  Card,
  CardContent,
  Typography,
  TextField,
  Fab,
  Snackbar,
  Alert,
} from '@mui/material';
import {
  Add as AddIcon,
  Remove as RemoveIcon,
} from '@mui/icons-material';
import { useState } from 'react';
import {
  useAppSelector,
  useAppDispatch,
} from '../hooks/redux';
import {
  increment,
  decrement,
} from '../store/slices/counterSlice';

function MuiDemo(): JSX.Element {
  const count = useAppSelector(
    (state) => state.counter.value
  );
  const dispatch = useAppDispatch();
  const [snackbarOpen, setSnackbarOpen] = useState(false);
  const [inputValue, setInputValue] = useState('');

  const handleIncrement = () => {
    dispatch(increment());
    setSnackbarOpen(true);
  };

  return (
    <Box sx={{ padding: 3 }}>
      <Typography variant='h4' gutterBottom>
        Material-UI Integration Demo
      </Typography>

      <Card sx={{ maxWidth: 400, margin: 'auto' }}>
        <CardContent>
          <Typography
            variant='h5'
            component='div'
            gutterBottom
          >
            カウンター: {count}
          </Typography>

          <Box
            sx={{
              display: 'flex',
              gap: 2,
              marginBottom: 2,
            }}
          >
            <Fab
              color='primary'
              size='small'
              onClick={handleIncrement}
            >
              <AddIcon />
            </Fab>
            <Fab
              color='secondary'
              size='small'
              onClick={() => dispatch(decrement())}
            >
              <RemoveIcon />
            </Fab>
          </Box>

          <TextField
            label='メモ'
            variant='outlined'
            fullWidth
            value={inputValue}
            onChange={(e) => setInputValue(e.target.value)}
            sx={{ marginBottom: 2 }}
          />

          <Button
            variant='contained'
            fullWidth
            onClick={() => setInputValue('')}
          >
            クリア
          </Button>
        </CardContent>
      </Card>

      <Snackbar
        open={snackbarOpen}
        autoHideDuration={2000}
        onClose={() => setSnackbarOpen(false)}
      >
        <Alert severity='success'>
          カウンターが増加しました!
        </Alert>
      </Snackbar>
    </Box>
  );
}

export default MuiDemo;

実際の開発フロー体験

実際の開発フローを通じて、Vite と React の組み合わせの威力を体験しましょう。

プロジェクト統合と最終確認

typescript// src/main.tsx - 全機能統合版
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import {
  ThemeProvider,
  createTheme,
} from '@mui/material/styles';
import { CssBaseline } from '@mui/material';
import { store } from './store';
import App from './App.tsx';
import './index.css';

const theme = createTheme({
  palette: {
    primary: {
      main: '#646cff',
    },
    secondary: {
      main: '#f50057',
    },
  },
});

ReactDOM.createRoot(
  document.getElementById('root')!
).render(
  <React.StrictMode>
    <Provider store={store}>
      <BrowserRouter>
        <ThemeProvider theme={theme}>
          <CssBaseline />
          <App />
        </ThemeProvider>
      </BrowserRouter>
    </Provider>
  </React.StrictMode>
);

最終的なアプリケーション

typescript// src/App.tsx - 統合版アプリケーション
import {
  Routes,
  Route,
  Link,
  useLocation,
} from 'react-router-dom';
import {
  AppBar,
  Toolbar,
  Typography,
  Button,
  Container,
} from '@mui/material';
import ReduxCounter from './components/ReduxCounter';
import MuiDemo from './components/MuiDemo';

function Home() {
  return (
    <Container>
      <Typography variant='h3' gutterBottom>
        Vite + React 統合デモ
      </Typography>
      <Typography variant='body1'>
        このアプリケーションは以下の技術スタックを統合しています:
      </Typography>
      <ul>
        <li>Vite - 高速ビルドツール</li>
        <li>React + TypeScript - UIフレームワーク</li>
        <li>React Router - SPAルーティング</li>
        <li>Redux Toolkit - 状態管理</li>
        <li>Material-UI - UIコンポーネントライブラリ</li>
      </ul>
    </Container>
  );
}

function Navigation() {
  const location = useLocation();

  return (
    <AppBar position='static'>
      <Toolbar>
        <Typography variant='h6' sx={{ flexGrow: 1 }}>
          Vite React Demo
        </Typography>
        <Button
          color='inherit'
          component={Link}
          to='/'
          variant={
            location.pathname === '/' ? 'outlined' : 'text'
          }
        >
          ホーム
        </Button>
        <Button
          color='inherit'
          component={Link}
          to='/counter'
          variant={
            location.pathname === '/counter'
              ? 'outlined'
              : 'text'
          }
        >
          Redux Counter
        </Button>
        <Button
          color='inherit'
          component={Link}
          to='/mui-demo'
          variant={
            location.pathname === '/mui-demo'
              ? 'outlined'
              : 'text'
          }
        >
          MUI Demo
        </Button>
      </Toolbar>
    </AppBar>
  );
}

function App(): JSX.Element {
  return (
    <>
      <Navigation />
      <Container sx={{ marginTop: 3 }}>
        <Routes>
          <Route path='/' element={<Home />} />
          <Route
            path='/counter'
            element={<ReduxCounter />}
          />
          <Route path='/mui-demo' element={<MuiDemo />} />
        </Routes>
      </Container>
    </>
  );
}

export default App;

開発体験の確認

bash# 開発サーバーの起動(2-3秒で完了)
yarn dev

# ビルドと最適化確認
yarn build

# プレビューモードでの動作確認
yarn preview

この統合デモアプリケーションにより、Vite の高速開発環境と React エコシステムの強力な組み合わせを実際に体験していただけます。コンポーネントの変更が瞬時に反映され、TypeScript による型安全性、Redux Toolkit による予測可能な状態管理、Material-UI による美しい UI が統合された、プロフェッショナルな開発環境が構築されました。

まとめ

Vite で React プロジェクトを立ち上げることは、現代のフロントエンド開発において最も効率的で将来性のあるアプローチです。従来の Create React App と比較して、開発速度、カスタマイズ性、そして開発者体験のすべてにおいて優位性を持っています。

技術的優位性の面では、ESModules ベースのアーキテクチャによる瞬時の起動、React Fast Refresh による状態保持型のホットリロード、そして TypeScript との深い統合により、ストレスフリーな開発環境を実現できました。特に大規模な React アプリケーション開発において、その差は顕著に現れます。

実践的な統合能力も際立っています。React Router、Redux Toolkit、Material-UI などの主要な React エコシステムライブラリとの統合が簡潔で直感的に行えることを実際に確認していただきました。段階的なプロジェクト成長にも柔軟に対応でき、小規模な実験的プロジェクトから大規模なエンタープライズアプリケーションまで、同じ設定アプローチで対応できるのです。

開発フローの改善により、コードの変更から結果確認までの時間が劇的に短縮され、開発者の集中力と創造性を最大限に引き出すことができます。Yarn を活用した効率的な依存関係管理と組み合わせることで、チーム開発においても一貫した高品質な開発体験を提供できるでしょう。

今後の React 開発において、Vite は必須のツールとなることは間違いありません。この記事で紹介した手順とベストプラクティスを活用して、より効率的で楽しい React 開発を始めていただければと思います。

関連リンク