開発

Preactとは?Reactとの違い・導入手順・compat移行と採用判断【2026年9月版】

Preactとは?Reactとの違い・導入手順・compat移行と採用判断【2026年9月版】

Preactは、Reactとほぼ同じ書き味を保ったまま、コアを数kBに抑えたUIライブラリです。公式サイトは「Fast 3kB alternative to React with the same modern API」と掲げています。npm registryで2026年9月27日に確認した最新版は10.29.8(公開2026年8月1日)で、次期メジャーの11系はリリース候補版の11.0.0-rc.2(2026年9月8日公開)まで進んでいました。この記事では、配布ファイルを実測したサイズの内訳、手元で最小アプリを動かす手順、合成イベントを持たないことから生じる書き換え箇所、preact/compatでReact資産を移す設定、signalsとフックの責務分担、そして受託開発で採用してよい案件と見送る案件の線引きまでを、コード付きで扱います。バンドルサイズを一桁小さくしたい場面で、どこまでReactの書き方が通用するかが判断の軸になります。

まとめ:Preactを採用してよい条件と見送るべき案件の線引き

結論から示します。Preactを採る価値があるのは、配信バイト数が体験や計測値に直結する画面、具体的には外部サイトへ埋め込むウィジェット、単発のキャンペーンページ、通信環境の悪い現場で使う入力端末向け画面です。数十kB規模になるReact本体とReactDOMの組み合わせが、gzip後数kBのコアに置き換わるため、初回描画までの転送量の差がそのまま効きます。

逆に見送るべき案件もはっきりしています。React Server Componentsを前提とした構成、React Nativeとコンポーネントを共有する計画、社内に大量のReact向け社内ライブラリが既にある環境。この3つに当たる場合、preact/compatで吸収しきれない差分の調査に時間が溶け、削減できる数十kBに見合いません。バージョンは10.29系を選び、11系のリリース候補版は検証環境に留める。以降の章では、この判断を支える実装差分と数値を、動かせるコードとあわせて順に見ていきます。

Preactとは何か|Reactサブセットとしての立ち位置と3kB構成の中身

PreactはReactのフォークではなく、同じコンポーネントモデルと仮想DOMの考え方を独自に実装し直したライブラリです。APIの形はReactに寄せてありますが、内部の作りは別物になります。ソースコードはGitHubのpreactjs/preactリポジトリでMITライセンスとして公開されており、フックはpreact/hooks、React互換層はpreact/compatというサブパスに分かれています。

公称3kBと配布ファイルをgzipで実測したサイズの内訳とReactとの差

Preactが3kBを名乗れるのは、Reactが内部に抱えている機能の一部を最初から持たないためです。代表格が後述する合成イベントシステムで、Preactはブラウザ標準のaddEventListenerをそのまま呼びます。スケジューラやプロファイラ向けの計装も本体には含みません。

公称値だけでは見積りに使いにくいので、npm registryから10.29.8のtarballを取得し、同梱の配布ファイルをgzip(圧縮レベル9)にかけて測りました。2026年9月27日時点の実測値は次のとおりです。

配布ファイル(10.29.8) 未圧縮 gzip後
preact.min.js(コア) 約11.3kB 約4.8kB
hooks.module.js 約3.6kB 約1.5kB
compat.module.js 約10.3kB 約4.1kB
jsxRuntime.module.js 約1.8kB 約1.0kB

表のファイル名に対応する配布パスは、コアがdist/preact.min.js、フックがhooks/dist/hooks.module.js、互換層がcompat/dist/compat.module.js、JSXランタイムがjsx-runtime/dist/jsxRuntime.module.jsです。

配布ファイル単体のgzipでは公称の3kBより大きく出ます。バンドラのtree shakingで使わない関数が落ちるため、実際のアプリに入る量はこれより小さくなる場合があります。一方、同じ日に取得したReact 19.3.0は本番用ファイルをminify前の状態で配布しており、react-domのクライアント用本番ファイル(react-dom-client.production.js)は未圧縮で約625kB、gzip後でも約110kBありました。minifyを経れば縮みますが、数十kB規模は残ります。コアとフックだけで組むPreactとの差は、一桁kB対数十kBという桁の違いとして捉えるのが妥当です。compatを足すと差は縮みますが、それでも大きな削減が残ります。厳密な差は構成で変わるため、導入前後で自分のバンドル解析結果を比べてください。

npm実測で見る10.29系の現在地とrc・beta・legacyタグの扱い

バージョンの現在地を数字で押さえておきます。2026年9月27日時点のnpm registryでは、latestタグが10.29.8(2026年8月1日公開)、rcタグが11.0.0-rc.2(2026年9月8日公開)、betaタグが11.0.0-beta.2(2026年7月15日公開)、legacyタグが8.5.3でした。10.29.8のリリースノートはflushSync内の更新のバッチ化など性能改善2件で、10.29.5から10.29.7は7月7日から8日にかけて立て続けに出ています。パッチの間隔は数週間から1日単位まで幅があるので、実務では10.29系をキャレット指定で追う運用が現実的です。legacyの8系は2017年前後のプロジェクトを触るとき以外に選ぶ理由がありません。

仮想DOMを保ちながらDOM APIへ直結させた設計の考え方

Preactは仮想DOMそのものは捨てていません。差分計算を行い、必要な箇所だけ実DOMへ反映する流れはReactと同じです。違うのは抽象化の層の厚みで、公式サイトは「the thinnest possible Virtual DOM abstraction」と表現しています。イベントも属性もブラウザのAPIへ素直に渡す設計です。この設計のおかげで、DOMを直接触る既存スクリプトやWeb Componentsとの同居が素直に進みます。標準のカスタム要素でUI部品を配る方向を検討しているなら、LitでWeb Componentsを作る使い方と比べると、Preactで組んだ画面にどこまで部品を差し込めるかの見通しが立ちます。同じ「軽量」を名乗るライブラリでも、仮想DOMを持たずHTML属性で状態を書くAlpine.jsの18ディレクティブによる実装とは設計の出発点が異なり、コンポーネント分割の粒度にも違いがある設計です。

Preactを試す|npm init preactとViteで最小アプリを動かす手順

判断の前に、手元で一度動かしておくと差分の感覚がつかめます。ここでは公式の雛形生成と、既存のVite構成へ足す方法の2通りを示します。

npm init preactで雛形を作りカウンターを画面に表示するまでの流れ

公式のGetting Startedが案内しているのは、npm init preactによる雛形生成です。対話形式でTypeScriptの有無やルーターの要否を選ぶと、Viteベースのプロジェクトが作られます。

npm init preact
cd my-preact-app
npm install
npm run dev

生成されたsrc配下のエントリを、次のカウンターに置き換えると動作を確かめられます。import元がreactではなくpreactとpreact/hooksである点と、renderをpreact本体から呼ぶ点がReactとの違いです。

import { render } from 'preact';
import { useState } from 'preact/hooks';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button class="counter" onClick={() => setCount(count + 1)}>
      クリック数: {count}
    </button>
  );
}

render(<Counter />, document.getElementById('app'));

ボタンにclassNameではなくclassを書いても動きます。この緩さは次の章で扱う属性の差分に関わります。

既存Viteへの@preact/preset-vite追加設定とtsconfig指定

既にViteのプロジェクトがある場合は、公式のプリセット@preact/preset-viteを入れます。npm registryでの最新版は2.10.6(2026年7月18日公開)でした。プラグインを1行足すだけで、JSXの変換先、HMR(Prefresh)、DevToolsの接続がまとめて設定されます。

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

export default defineConfig({
  plugins: [preact()],
});

このプリセットはreactAliasesEnabledが既定でtrueになっており、reactとreact-domのimportを自動でpreact/compatへ向けます。React向けのライブラリを混ぜないならpreact({ reactAliasesEnabled: false })と明示しておくと、意図しない互換層の混入に気づけます。TypeScriptでは、公式のTypeScriptガイドに従い、JSXの変換先として指定するのはpreactです。

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "preact"
  }
}

Vite自体の環境構築やバージョンごとの変更点は、Viteの環境構築手順とVite 8の変更点にまとめています。ReactのViteテンプレートから出発してプラグインを差し替える流れで読むと、両者の設定差が見えます。

ReactとPreactの実装差分|イベント・属性・非対応APIの具体

移行コストの実体は、この章に挙げる差分の数で決まります。公式ガイドDifferences to Reactに明記されている項目を、書き換えが発生する順に並べます。

合成イベント非搭載によるonInputとonDblClickへの書き換え

PreactはReactの合成イベント(Synthetic Events)を実装していません。公式ガイドは、サイズと性能の理由からブラウザ標準のaddEventListenerを使うと説明しています。実装上の影響が最も大きいのはフォームで、ReactのonChangeに相当するものはonInputになります。ダブルクリックもonDoubleClickではなくonDblClickです。さらにPortalを越えてイベントがバブリングしない点も明記されており、モーダル内のクリックを外側で拾う設計を持ち込むと動きません。preact/compatを噛ませればonChangeは内部でonInputへ変換されますが、Portal越しのバブリングは別問題として残ります。

compatを使わない場合、入力欄は次のように書きます。e.currentTargetの型がHTMLInputElementとして扱われるよう、イベント型をpreactから取る点も押さえておきます。

import { useState } from 'preact/hooks';
import type { TargetedEvent } from 'preact';

export function NameField() {
  const [name, setName] = useState('');
  const onInput = (e: TargetedEvent<HTMLInputElement, Event>) =>
    setName(e.currentTarget.value);

  return (
    <label>
      氏名
      <input value={name} onInput={onInput} onDblClick={() => setName('')} />
    </label>
  );
}

Reactから写経したコードでonChangeのまま残すと、入力のたびではなくフォーカスが外れたときだけ状態が更新されます。ブラウザ標準のchangeイベントの挙動そのものなので、エラーにならず気づきにくい点に注意してください。

classとclassNameの併用可否とSVG属性をそのまま書ける利点

属性の扱いはPreactのほうが緩く、classとclassNameのどちらも受け付ける仕様です。公式ガイドは、Preactの開発者の多くがclassを好むと書いています。SVGについては「属性を書かれたとおりに適用する」と述べており、デザイナーから受け取ったSVGをキャメルケースへ直さずに貼れます。stroke-widthやfill-ruleをそのまま置けるため、アイコンを大量に扱う画面では効果が明確です。ただしReactへ戻す可能性がある資産では、あえてclassNameとキャメルケースで書き揃えておくほうが後の手戻りが減ります。

Preact本体が持たない機能と互換レイヤで補える範囲の対応表

本体だけで足りるか、compatが要るかを事前に仕分けておくと見積りがぶれません。主要な項目を整理します。

項目 Preact本体 preact/compat
合成イベント 非搭載(標準API) onChangeを変換
PropTypes 本体に含まない 互換層で補完
Children API 本体に含まない 互換層で補完
contextTypes宣言 不要(全て伝播) 宣言しても無害
Portal内のバブリング 起きない 起きない
class属性の直書き 可能 可能

表の最終行だけは差分ではなく、Reactから来た人が驚く箇所として挙げました。contextTypesの宣言が要らないのは、getChildContextが生んだ値を全コンポーネントが受け取る仕様のためです。なお11系のリリース候補版では、11.0.0-rc.0のリリースノートにあるとおりcreatePortalが本体に入り、compatにはuseやuseEffectEventが追加されました。10.29系で書く場合は、表の内容をそのまま前提にしてください。

preact/compatによるReact資産の移行手順とエイリアス設定

既存のReactコードを動かす場合、コードを書き換えるのではなく解決先を差し替えます。公式サイトはこの互換層を、複雑なReactコンポーネントでもPreactで動かすための/compatエイリアスと説明しています。

Viteとwebpackで解決先を差し替える手順と検証の順番

導入は次の順で進めると戻しやすくなります。

  1. preactと互換層のパッケージを追加し、reactとreact-domは削除せず残す
  2. ビルド設定のresolve.aliasで、reactの解決先をpreact/compatへ向ける
  3. TypeScriptのpathsにも同じ対応を書き、型解決をビルドと揃える
  4. フォーム、モーダル、外部UIライブラリの3系統を先に手動確認する
  5. 問題が出た画面だけaliasから除外し、Reactのまま残すか書き換えるかを判断する

Viteなら前章の@preact/preset-viteが手順2を自動で行います。webpackの場合は、Getting Startedに載っている次の設定をそのまま使えます。この設定は、React本体だけでなく、テストユーティリティとJSXランタイムの解決先もPreact側へ揃えるための指定です。

// 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',
    },
  },
};

手順3のTypeScript側は、tsconfig.jsonのpathsで同じ対応を書きます。ここが漏れると、ビルドは通るのにエディタ上では@types/reactの型で補完される、というずれが起きます。

{
  "compilerOptions": {
    "skipLibCheck": true,
    "baseUrl": "./",
    "paths": {
      "react": ["./node_modules/preact/compat/"],
      "react/jsx-runtime": ["./node_modules/preact/jsx-runtime"],
      "react-dom": ["./node_modules/preact/compat/"],
      "react-dom/*": ["./node_modules/preact/compat/*"]
    }
  }
}

aliasを戻せば元のReactビルドに復帰できるため、いきなり全画面を切り替えず、ルーティング単位で試すのが安全です。

React資産の移行で詰まりやすいテスト系パッケージと型定義のずれ

詰まる箇所には傾向があります。react-dom/test-utilsやReactの内部構造に触れるテストユーティリティ、Reactのバージョン判定で分岐する外部UIライブラリ、そして@types/reactを前提に型付けされた社内コンポーネントの3つです。特にReact 19で追加されたAPIに依存したコードは互換層の守備範囲外に落ちやすく、レンダリングの前段でエラーになります。React 19で追加された機能の一覧を先に照らし、依存している新APIを洗い出してから移行判断に入ると空振りが減ります。npmのlatestであるReactは2026年9月27日時点で19.3.0でした。互換層が報告するReactのバージョンは11系のリリース候補版で19.0.0へ引き上げられた段階で、10.29系ではさらに古い版として振る舞う前提で外部ライブラリの分岐を確認してください。

React資産の移行を進める損益分岐と途中で引き返すべき判定条件

判断の目安を数字で置きます。aliasを当てて主要3系統の確認が半日で終わるなら進めてよい。逆に、外部UIライブラリの差し替え検討が発生した時点で引き返すべきです。UIライブラリ1本の置き換えは、削減できる数十kBに対して数人日を要することが多く、割に合いません。既存コードが少ない新規開発なら、最初からPreactで書き始めるほうが移行作業そのものを消せます。

@preact/signalsとフックの使い分け|状態管理の実装判断

状態管理には、Reactと同じフックに加えてsignalsという選択肢があります。どちらか一方に寄せる必要はありません。

signalsパッケージの公開状況と再描画範囲が変わる仕組みの違い

npm registryで2026年9月27日に確認したところ、@preact/signalsの最新版は2.11.2(2026年9月4日公開)でした。8月から9月にかけて2.11.0から2.11.2までが出ており、更新は活発です。ソースはpreactjs/signalsリポジトリで管理されています。signalsは値の読み取りを追跡し、その値を実際に使っている箇所だけを更新する仕組みです。コンポーネント全体を再実行するフックの再描画とは、更新の粒度が違います。

公式のSignalsガイドの例を下敷きにすると、最小構成は次のとおりです。

import { signal, computed } from '@preact/signals';

const count = signal(0);
const doubled = computed(() => count.value * 2);

export function Counter() {
  return (
    <div>
      <p>回数: {count}</p>
      <p>2倍: {doubled}</p>
      <button onClick={() => count.value++}>増やす</button>
    </div>
  );
}

要点は、JSXにcount.valueではなくcountそのものを渡している箇所です。公式ガイドによれば、シグナルをテキストの位置へ直接渡すと、仮想DOMの差分計算を経ずにそのテキストノードだけが更新されます。.valueで読むとコンポーネントの再描画が起きるため、この書き方の違いが、更新範囲を変える理由です。依存追跡を意図的に外すuntrackedや、派生値を作るcomputedの挙動は、untrackedとcomputedシグナルの使い分けで他フレームワークの実装と並べて整理しています。コンポーネントが一度しか実行されない前提でシグナルを設計したSolidJSとの違いは、SolidJSがSignalで速い理由と読み比べると、Preactが仮想DOMとシグナルを併用している位置づけがわかります。

signalsを入れる場面とフックのままで足りる場面の線引き

signalsを入れる価値があるのは、1秒間に何度も値が変わる表示、たとえばリアルタイム集計やカーソル追従、進捗バーを持つ画面です。逆に、フォーム入力と送信が中心の管理画面ではuseStateで十分で、signalsを足すとレビュー時の読解コストだけが増えます。数kBのコアを選んだプロジェクトに、追加の依存を無条件で足すのは筋が通りません。判断は画面ごとに分けるのが現実的です。

Preactが向く案件と外れる案件|受託開発での採用条件の線引き

ここからは、実際に見積りを立てる側の視点で判断を言い切ります。技術的に動くかどうかと、案件として採るべきかは別の問題です。

Preactを採用してよい案件の条件と配信サイズ削減が効く画面の共通点

採用してよいのは、次の条件に当てはまる案件です。第一に、第三者サイトへ埋め込むウィジェットのように、こちらが配信するJavaScriptの量に制約がある場合。第二に、島単位でUIを差し込む構成をとる場合で、Deno FreshのアイランドアーキテクチャのようにPreactを前提とするフレームワークを採るときは選択自体が済んでいます。島ごとにJavaScriptを配る考え方そのものは、アイランドアーキテクチャの仕組みとAstroの実装で整理しており、AstroでもPreactを島の描画に選べます。第三に、通信が細い現場端末向けの画面。いずれも「初回に送るバイト数」が体験に直結する点が共通します。

Preact採用を見送る案件の条件と保守で破綻しやすい構成の共通型

見送るべき構成も明示します。React Server Componentsを使う前提の設計、React Nativeとコンポーネントを共有する計画、Reactのバージョンを厳密に前提とした有償UIコンポーネントを組み込む案件。この3つでPreactを押し通すと、互換層の穴を塞ぐ改修が積み上がり、削減した数十kBの価値を人件費が上回ります。「軽いから」だけを根拠に選ぶ判断は、ここで破綻します。React本体でよい場面のほうが依然として多い、というのが実務上の結論です。

開発見積りと保守計画で織り込むべき互換レイヤの継続運用コスト

互換層を挟む構成は、保守フェーズで固有の作業を生みます。Reactの新バージョンが出るたびに互換層の追随状況を確認する手間、ライブラリ更新時に本体とcompatのどちらの問題かを切り分ける手間です。この工数を見積りへ最初から織り込んでおくと、後の追加請求をめぐる衝突を避けられます。フロントエンドの構成選定から保守までを含めて外部に任せる前提であれば、業務用・Webアプリ開発の受託のように、技術選定の根拠と運用体制をセットで提示できる相手を選ぶと判断がぶれません。

Preact 11のリリース候補版の現状と本番開発で10.29系を選ぶ理由

次期メジャーの扱いも、選定時に必ず聞かれる論点です。数字で状態を確認しておきます。

11.0.0-rc.2の到達点と本番投入をまだ見送る判断の根拠

11系は2026年7月15日のbeta.2から、8月10日のrc.0、8月26日のrc.1、9月8日のrc.2とリリース候補版を重ねています。11.0.0-rc.2のリリースノートには、Suspenseが再度suspendした際にフックの状態を保持する修正や、エラーバウンダリの判定の見直しに加え、削除済みのminビルドへの参照を整理する項目が並んでいます。2026年9月27日時点でlatestタグは10.29.8のままで、11系はまだ安定版として配布されていません。

リリース候補版の段階に入ったことで、大きな破壊的変更が追加される余地は小さくなりました。それでも修正が毎回複数件入っている状況では、受託案件で納品する構成に選ぶ理由は見当たりません。検証環境で挙動を見ておく価値はありますが、本番の依存に入れる判断は安定版の公開を待ちます。

10.29系で開発しながら11系への移行余地を残す実装規約と書き方

将来の移行を軽くする書き方は決まっています。preact/compat経由の呼び出しを一箇所にまとめ、独自のラッパーコンポーネントを挟んでおく。CDNや配布パスでdist/preact.min.jsを直接指している箇所は、11系でminビルドが無くなる前提でパッケージのexports経由のimportへ寄せておく。加えて、レンダリング方式の選択を本体のコードから切り離しておくと、後から構成を差し替えやすくなります。SSR・CSR・SSGの違いと使い分けを踏まえ、どの方式で配信するかを先に固めてからライブラリを選ぶ順序にすると、メジャーアップデートの影響範囲が読めます。

よくある質問

Preactの採用検討でよく挙がる質問に、実装と判断の両面から答えます。

PreactはReactの完全な代替として全面採用できますか?

全面採用が成立する条件は限定的です。Preact単体ではReactの合成イベントやPropTypes、Children APIを持たず、preact/compatを噛ませても完全な互換は保証されません。新規開発でReact専用の外部ライブラリに依存しない構成なら全面採用できます。既存のReactアプリを丸ごと置き換える場合は、外部UIライブラリとテストユーティリティの2点を先に検証し、通らなければ画面単位の部分適用に留めるのが安全です。

Preactの3kBという数値には何が含まれていますか?

3kBは公式サイトが掲げるコアの公称値で、互換層のpreact/compatや、@preact/signalsなどの追加パッケージは含みません。10.29.8の配布ファイルを実測すると、コアのpreact.min.jsはgzip後約4.8kB、hooksが約1.5kB、compatが約4.1kBでした。実際にアプリへ入る量はtree shakingとビルド構成で変わるため、導入前後でバンドル解析ツールの出力を比較して確認してください。

Next.jsやReact Server ComponentsはPreactで動きますか?

React Server Componentsを前提とする構成はPreactの守備範囲外と考えてください。サーバー側でコンポーネントを実行しシリアライズする仕組みはReact本体と密結合しており、互換層で吸収できる範囲を超えます。RSCの仕組み自体はReact Server Componentsのサーバー・クライアントの違いで解説しています。サーバーレンダリングを含む構成でPreactを使うなら、Deno FreshやAstroのようにPreactを前提に設計されたフレームワークを選ぶほうが破綻しません。

PreactとAlpine.jsはどちらを選ぶべきですか?

判断軸は「コンポーネントを組み立てるか、既存HTMLへ振る舞いを足すか」です。JSXでUIを分割し、状態を持つ画面を作るならPreactが適任です。サーバーが返すHTMLに対して開閉やタブ切り替えを足すだけなら、Alpine.jsのほうが記述量が少なく済みます。Reactの経験者が多いチームではPreactのほうが学習コストが低く、テンプレート中心の運用チームでは逆になります。

Preact 11の安定版が出るまで採用を待つべきですか?

待つ必要はありません。2026年9月27日時点で10.29.8がlatestとして配布されており、パッチも継続的に出ています。11系はリリース候補版の11.0.0-rc.2まで進みましたが、安定版の公開日は告知されていません。今作るなら10.29系で進め、compat経由の呼び出しを一箇所へ集め、minビルドを直接参照しない書き方にしておくことで、11系が安定した後の移行作業を小さくできます。

関連記事

お気に入りに入れた記事の一覧

一創の公式 SNS

X では毎朝の新着記事と IT ニュースの速報を、YouTube・TikTok・Instagram では記事の解説動画を配信しています。

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.28 テックブログ リンツ不正アクセス:314万件の対象確認とパスワード強制リセット後の対処
  2. 2026.10.29 テックブログ ソニー生命の不正アクセス:38.5万件の対象確認と窓口、停止中の手続き
  3. 2026.10.27 テックブログ Brotli 脆弱性:CVE-2026-111497の影響確認とNode.js・Python・.NETの更新手順
  4. 2026.01.30 コラム 税務調査は個人と法人で何が違う?担当部署・件数・調べられる点と追徴税額を比較【令和6事務年度】
  5. 2026.09.26 コラム 価格転嫁とは?価格交渉促進月間の調査に回答義務はあるか・取適法の協議記録まで

RELATED POSTS 関連記事

目次