ブログ記事

Preact実践ガイド 2025 - 3kBの軽量React代替ライブラリ

わずか3kBで実現する高速なReact代替ライブラリPreactを徹底解説。本番環境での導入方法、パフォーマンス最適化、React互換レイヤーの活用法まで、実践的な知識を完全網羅します。

Web開発
Preact React JavaScript フロントエンド パフォーマンス
Preact実践ガイド 2025 - 3kBの軽量React代替ライブラリのヒーロー画像

Preact は、Reactの現代的な API を保ちながら、わずか3kBのバンドルサイズを実現する軽量フレームワークです。2025 年 6 月時点で 37,000 以上の GitHubスターを獲得し、パフォーマンスを重視する多くのプロダクションサイトで採用されています。

この記事で学べること

  • Preact v10.26 の最新機能と改善点
  • Reactからの移行方法と互換性レイヤーの活用
  • パフォーマンス最適化のベストプラクティス
  • 本番環境での実装パターンと注意点
  • 実際のプロジェクトでの活用事例

なぜPreactを選ぶのか?

バンドルサイズの肥大化

React + ReactDOM = 約45kB (gzipped)

初期ロード時間

モバイル環境での遅延

Preactの登場

3kBで同等の機能を実現

エコシステムの成熟

主要ライブラリとの互換性確立

Preact vs React:詳細比較

Preact vs React 機能比較(2025年6月時点)
特徴 Preact React 差異
バンドルサイズ 3kB 45kB -93%
Virtual DOM 薄い抽象化 フル機能 高速化
ブラウザ互換性 IE11+ IE11+ 同等
エコシステム compat経由 ネイティブ 互換レイヤー
学習曲線 低い 中程度 React経験者は即座に使用可
パフォーマンス 優秀 良好 軽量化による高速化

クイックスタート

1. インストールと基本セットアップ

# npmでインストール
npm install preact

# TypeScript使用時
npm install -D @types/node

# React互換レイヤー(必要に応じて)
npm install preact-compat
<!-- 開発版 -->
<script type="module">
  import { h, render } from 'https://unpkg.com/preact?module';
  
  const App = () => h('h1', null, 'Hello Preact!');
  render(h(App), document.body);
</script>

<!-- プロダクション版 -->
<script src="https://unpkg.com/preact/dist/preact.min.js"></script>
# Viteプロジェクトの作成
npm create vite@latest my-preact-app -- --template preact

# TypeScript版
npm create vite@latest my-preact-app -- --template preact-ts

cd my-preact-app
npm install
npm run dev

2. 基本的なコンポーネント実装

import React, { useState, useEffect } from 'react'; function Counter() { const [count, setCount] = useState(0); useEffect(() => { console.log('Count updated:', count); }, [count]); return ( <div> <h2>Count: {count}</h2> <button onClick={() => setCount(count + 1)}> Increment </button> </div> ); }
import { h } from 'preact'; import { useState, useEffect } from 'preact/hooks'; function Counter() { const [count, setCount] = useState(0); useEffect(() => { console.log('Count updated:', count); }, [count]); return ( <div> <h2>Count: {count}</h2> <button onClick={() => setCount(count + 1)}> Increment </button> </div> ); }
Reactコンポーネント
import React, { useState, useEffect } from 'react'; function Counter() { const [count, setCount] = useState(0); useEffect(() => { console.log('Count updated:', count); }, [count]); return ( <div> <h2>Count: {count}</h2> <button onClick={() => setCount(count + 1)}> Increment </button> </div> ); }
Preactコンポーネント
import { h } from 'preact'; import { useState, useEffect } from 'preact/hooks'; function Counter() { const [count, setCount] = useState(0); useEffect(() => { console.log('Count updated:', count); }, [count]); return ( <div> <h2>Count: {count}</h2> <button onClick={() => setCount(count + 1)}> Increment </button> </div> ); }

React互換性レイヤーの活用

Preact互換性アーキテクチャ

チャートを読み込み中...

webpack設定での互換性設定

// webpack.config.js
module.exports = {
  resolve: {
    alias: {
      "react": "preact/compat",
      "react-dom/test-utils": "preact/test-utils",
      "react-dom": "preact/compat",
      "react/jsx-runtime": "preact/jsx-runtime"
    }
  }
};

Vite設定での互換性設定

// vite.config.js
import { defineConfig } from 'vite';
import preact from '@preact/preset-vite';

export default defineConfig({
  plugins: [preact()],
  resolve: {
    alias: {
      react: 'preact/compat',
      'react-dom': 'preact/compat'
    }
  }
});

パフォーマンス最適化テクニック

1. コンポーネントの最適化

import { h } from 'preact';
import { memo } from 'preact/compat';

// メモ化されたコンポーネント
const ExpensiveList = memo(({ items }) => {
  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}, (prevProps, nextProps) => {
  // カスタム比較関数
  return prevProps.items.length === nextProps.items.length;
});

// 遅延ロード
import { lazy, Suspense } from 'preact/compat';

const HeavyComponent = lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <HeavyComponent />
    </Suspense>
  );
}

2. バンドルサイズの最適化

バンドルサイズ削減率 93 %
// 必要な機能のみをインポート
import { render } from 'preact';
import { useState } from 'preact/hooks';

// 不要な機能を避ける
// ❌ import * as preact from 'preact';
// ✅ import { h, render } from 'preact';

// プロダクションビルドの最適化
// package.json
{
  "scripts": {
    "build": "vite build --minify terser",
    "analyze": "vite-bundle-visualizer"
  }
}

実践的な実装パターン

1. 状態管理の実装

import { h, createContext } from 'preact';
import { useContext, useReducer } from 'preact/hooks';

const StateContext = createContext();

function stateReducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { ...state, count: state.count + 1 };
    case 'decrement':
      return { ...state, count: state.count - 1 };
    default:
      return state;
  }
}

export function StateProvider({ children }) {
  const [state, dispatch] = useReducer(stateReducer, { count: 0 });
  
  return (
    <StateContext.Provider value={{ state, dispatch }}>
      {children}
    </StateContext.Provider>
  );
}

export const useAppState = () => useContext(StateContext);
import { create } from 'zustand';
import { h } from 'preact';

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
}));

function Counter() {
  const { count, increment, decrement } = useStore();
  
  return (
    <div>
      <h2>{count}</h2>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
    </div>
  );
}
import { signal, computed } from '@preact/signals';

// シグナルの作成
const count = signal(0);
const doubled = computed(() => count.value * 2);

function Counter() {
  return (
    <div>
      <p>Count: {count}</p>
      <p>Doubled: {doubled}</p>
      <button onClick={() => count.value++}>
        Increment
      </button>
    </div>
  );
}

2. ルーティングの実装

import { h } from 'preact';
import { Router, Route, Link } from 'preact-router';
import { createHashHistory } from 'history';

// ページコンポーネント
const Home = () => <h1>Home</h1>;
const About = () => <h1>About</h1>;
const Profile = ({ user }) => <h1>Profile: {user}</h1>;

// ルーティング設定
function App() {
  return (
    <div>
      <nav>
        <Link href="/">Home</Link>
        <Link href="/about">About</Link>
        <Link href="/profile/john">Profile</Link>
      </nav>
      
      <Router history={createHashHistory()}>
        <Route path="/" component={Home} />
        <Route path="/about" component={About} />
        <Route path="/profile/:user" component={Profile} />
      </Router>
    </div>
  );
}

本番環境での最適化

1. SSR(サーバーサイドレンダリング)

// server.js
import express from 'express';
import { h } from 'preact';
import render from 'preact-render-to-string';
import App from './App';

const app = express();

app.get('*', (req, res) => {
  const html = render(<App url={req.url} />);
  
  res.send(`
    <!DOCTYPE html>
    <html>
      <head>
        <title>Preact SSR</title>
      </head>
      <body>
        <div id="app">${html}</div>
        <script src="/bundle.js"></script>
      </body>
    </html>
  `);
});

app.listen(3000);

2. プログレッシブ強化

// クライアント側のハイドレーション
import { h, hydrate } from 'preact';
import App from './App';

// SSRされたコンテンツをハイドレート
if (typeof window !== 'undefined') {
  hydrate(<App />, document.getElementById('app'));
}

実プロジェクトでの活用事例

モバイル Web アプリケーションで Preact を採用することで、初期ロード時間を 50%削減し、ユーザーエンゲージメントが大幅に向上しました。

Uber Engineering フロントエンドチーム

主要な採用企業

  • Uber: モバイル Web アプリケーション
  • The New York Times: インタラクティブコンテンツ
  • Lyft: ドライバー向けダッシュボード
  • Pepsi: キャンペーンサイト

トラブルシューティング

よくある問題と解決策

  • React DevToolsが動作しない: preact/debug をインポートする
  • スタイルが適用されない: className ではなく class を使用
  • refが動作しない: createRef または useRef を使用
  • children propの型エラー: ComponentChildren 型を使用

まとめ

Preact は、パフォーマンスとバンドルサイズを重視するプロジェクトに最適な選択肢です。Reactの知識をそのまま活かしながら、93%のサイズ削減を実現できます。

特に以下のようなケースで Preact の採用を検討してください:

  • モバイルファーストの Web アプリケーション
  • パフォーマンスクリティカルなランディングページ
  • 組み込み Web ビューアプリケーション
  • プログレッシブ Web アプリ(PWA)

この記事は役に立ちましたか?

Daily Hackでは、開発者の皆様に役立つ情報を毎日発信しています。