はじめに
「Reactを使いたいけれど、バンドルサイズが気になる」——そんな悩みを抱えたことはありませんか。ランディングページやウィジェット、モバイル回線での表示速度がシビアなプロダクトでは、Reactのバンドルサイズがボトルネックになることがあります。
そこで候補に挙がるのがPreactです。Reactとほぼ同じAPIを持ちながら、コア部分はわずか3KB程度という驚異的な軽さを実現しています。今回はPreactの特徴から実際の使い方まで、手を動かしながら紹介します。
Preactとは
Preactは、Reactと同じモダンなAPI(Components、Hooks、JSX)を持ちながら、圧倒的に小さなフットプリントで動作する仮想DOMライブラリです。公式サイトでは「Fast 3kB alternative to React with the same modern API」と紹介されており、React学習で得た知識をほぼそのまま活かせるのが最大の魅力です。
主な特徴
- 超軽量 - コア部分は3KB程度に収まっており、ページ読み込み速度やパースコストを大幅に削減できます
- Reactと同じAPI -
useStateやuseEffectなどのHooksをはじめ、クラスコンポーネント・関数コンポーネントの両方に対応しています - preact/compatによる互換性 - バンドラーの設定にエイリアスを1行追加するだけで、既存のReactライブラリの多くをそのまま利用できます
- DOMに近い実装 - 標準的なプラットフォーム機能に寄せた設計になっており、実際のイベントハンドラを登録するなど、シンプルで予測しやすい動作をします
インストール
新規プロジェクトを始める場合は、公式が提供するCLIが便利です。
npm init preact
既存のプロジェクトに導入する場合は、パッケージを直接追加します。
# npm
npm install preact
# yarn
yarn add preact
# pnpm
pnpm add preact
JSXを使う場合はビルド設定にjsxのインポート元を指定する必要があります。Viteを使う場合は@preact/preset-viteを利用すると簡単です。
npm install --save-dev @preact/preset-vite
// vite.config.js
import { defineConfig } from "vite";
import preact from "@preact/preset-vite";
export default defineConfig({
plugins: [preact()],
});
基本的な使い方
まずはシンプルなカウンターコンポーネントを作ってみましょう。ReactのHooksをそのまま使っている感覚で書けます。
// Counter.jsx
import { useState } from "preact/hooks";
export function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>カウント: {count}</p>
<button onClick={() => setCount((c) => c + 1)}>+1</button>
<button onClick={() => setCount((c) => c - 1)}>-1</button>
</div>
);
}
エントリーポイントでの描画も、Reactとほぼ同じ書き方です。
// main.jsx
import { render } from "preact";
import { Counter } from "./Counter";
render(<Counter />, document.getElementById("app"));
useEffectやuseContextなど、よく使うHooksもpreact/hooksから同じ名前でインポートできます。
import { useEffect, useState } from "preact/hooks";
export function UserStatus({ userId }) {
const [isOnline, setIsOnline] = useState(false);
useEffect(() => {
const timer = setInterval(() => {
setIsOnline((prev) => !prev);
}, 3000);
return () => clearInterval(timer);
}, [userId]);
return <span>{isOnline ? "オンライン" : "オフライン"}</span>;
}
実践的なユースケース
既存のReactライブラリを使う
preact/compatを使うと、React向けに作られたUIライブラリの多くをそのまま利用できます。バンドラーにエイリアスを追加するだけです。
// 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",
},
},
});
これにより、reactやreact-domをimportしているサードパーティ製コンポーネントも、実行時にはPreactのランタイムで動作するようになります。
バンドルサイズがシビアな埋め込みウィジェット
自社サービスを他社サイトに埋め込むチャットウィジェットや広告ウィジェットのような、読み込み速度が直接ユーザー体験に影響するケースでは、Preactの軽さが特に活きます。
// widget.jsx
import { render } from "preact";
import { useState } from "preact/hooks";
function FeedbackWidget() {
const [sent, setSent] = useState(false);
const handleSubmit = (event) => {
event.preventDefault();
// フォーム送信処理はここに実装
setSent(true);
};
if (sent) {
return <p>フィードバックを送信しました。ありがとうございます!</p>;
}
return (
<form onSubmit={handleSubmit}>
<label>
ご意見をお聞かせください
<textarea name="feedback" required />
</label>
<button type="submit">送信</button>
</form>
);
}
const mountPoint = document.getElementById("feedback-widget");
if (mountPoint) {
render(<FeedbackWidget />, mountPoint);
}
このようなウィジェットをReactでビルドすると数十KB単位になりがちですが、Preactなら本体分のオーバーヘッドをほぼ無視できるレベルまで抑えられます。
静的サイトの部分的なインタラクティブ化
ほとんどが静的なコンテンツで、一部だけJavaScriptによる操作(アコーディオンやタブ切り替えなど)が必要なページにも向いています。ページ全体をSPA化せず、必要な箇所だけPreactでマウントすることで、軽快な読み込みを維持できます。
// accordion.jsx
import { render } from "preact";
import { useState } from "preact/hooks";
function Accordion({ title, children }) {
const [open, setOpen] = useState(false);
return (
<div class="accordion">
<button onClick={() => setOpen((prev) => !prev)}>
{title} {open ? "▲" : "▼"}
</button>
{open && <div class="accordion-content">{children}</div>}
</div>
);
}
document.querySelectorAll("[data-accordion]").forEach((el) => {
render(
<Accordion title={el.dataset.title}>{el.textContent}</Accordion>,
el
);
});
まとめ
Preactは、Reactと同じモダンなAPIを維持しながら、圧倒的な軽さを実現しているライブラリです。既存のReact知識をそのまま活かせるうえ、preact/compatによってエコシステムの多くの資産も利用できるため、乗り換えのハードルも低めです。
特に、埋め込みウィジェットやランディングページ、パフォーマンスがシビアに問われるプロダクトでは、Preactを検討する価値は十分にあります。まずは小さなコンポーネントから、npm init preactで試してみてはいかがでしょうか。