HonoのクライアントサイドレンダリングはReactと何が違うのか 🔥
RubyやPHPなどのサーバーサイドの言語とは違い、JavaScriptはクライアントサイド(ユーザーのブラウザ)でレンダリングを行うことができます。
かつてはVanilla JSやjQueryを使ってDOMを直接操作することで画面を更新していましたが、この方法には次のような課題がありました。
- ページ全体、あるいは大きなDOMを頻繁に再描画してしまう
- 処理が手続き的になりやすく、状態管理が複雑になる
- 規模が大きくなるほどコードの見通しが悪くなる
これらの問題を解決するために登場したのが、React や Vue といったコンポーネント指向のライブラリ・フレームワークです。
状態に応じて UI を宣言的に記述できるようになり、クライアントサイドレンダリング(CSR)は一気に主流になりました。
一方でHonoは基本的にサーバーサイドで HTMLを生成して返すフレームワークです。
しかし、Honoでもクライアントサイドレンダリングを行うことができます。
では、Hono のクライアントサイドレンダリングはReactのCSRと何が違うのでしょうか?
この記事では、その設計思想と使いどころの違いについて整理してみます。
ReactとHonoのクライアントサイドレンダリング ⚛️
React の特徴のひとつが JSX という HTML の拡張構文を使って UI を記述する点です。
React では、DOM を直接操作するのではなく、仮想DOM(Virtual DOM) を介して画面をレンダリング・更新します。
状態が変化すると仮想DOMが再生成され、前回との差分を計算した上で、必要な部分だけを実際のDOMに反映します。
これにより、複雑なUIでも宣言的に記述でき、状態管理もしやすくなりました。
Honoはサーバーサイドとクライアントサイドで異なる仕組み ⚙️
HonoもJSXを利用できますが、サーバーサイドではReactのような仮想DOMは採用していません、JSXを直接HTML文字列に変換しています。しかし、クライアントサイドレンダリングではReactと同様に仮想DOMを使用しています。
クライアントサイドでは軽量な仮想DOMを使用して、差分計算を行います。ただし、ReactのFiberのような複雑な構造ではなく、シンプルな構造になっているようです。
| モード | パッケージ | 仮想DOM | 処理内容 |
|---|---|---|---|
| サーバーサイド | hono/jsx |
❌ なし | JSX → HTML文字列を直接生成 |
| クライアントサイド | hono/jsx/dom |
✅ あり(軽量版) | JSX → NodeObject → DOM更新 |
Honoは高度な機能を省略することで、軽量でシンプルな仮想DOMを実現しています。
HonoのCSRはReactの実装を参考にしている
Honoのクライアントサイドレンダリング機能(hono/jsx/dom)は、ゼロから独自に設計されたものではなく、Reactの実装をベースに作られています。
GitHub上のソースコードには、Reactとの互換性や設計思想を示す記述が見られます。
1. react-dom/client との互換性
クライアントサイドのエントリーポイントとなる src/jsx/dom/client.ts には、互換性について記載されています。
This module provides APIs for hono/jsx/dom/client, which is compatible with react-dom/client. (このモジュールは react-dom/client と互換性のあるAPIを提供します)
これはつまり、createRootやhydrateRoot といった関数が、Reactと「同じ名前」「同じ挙動」で直感的に使えるように設計されていることを意味します。これまでReactで培った知識を捨てて新しい作法を覚える必要はなさそうです。
2. react-dom/server との互換性
サーバーサイドレンダリング用の src/jsx/dom/server.ts にも同様の記述があります。
This module provides APIs for hono/jsx/server, which is compatible with react-dom/server. (このモジュールは react-dom/server と互換性のあるAPIを提供します)
3. 内部実装(React Compilerへの言及)
仮想DOMのレンダリング処理を行う src/jsx/dom/render.tsにReact Compiler(Reactが自動でメモ化を行う仕組み)によって生成されたコードを想定したコメントが残されていました。
// The code generated by the react compiler is memoized under this condition.
// (React Compilerによって生成されたコードはこの条件下でメモ化されます)
これらの記述からも、HonoがReactのエコシステムや最新の仕様(React Compilerなど)に追従し、高い互換性を目指して開発されていることが分かります。 そのため、開発者はReactの知識をほぼそのまま活かしてHonoでのCSR実装を行うことができます。
Honoでクライアントサイドレンダリングするには?
tsconfig.jsonに記述してください。
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx/dom"
}
}
またはvite.config.tsに記述します。
import { defineConfig } from 'vite'
export default defineConfig({
esbuild: {
jsxImportSource: 'hono/jsx/dom',
},
})
サンプルコード
これはHonoドキュメントにあるシンプルなカウンターを作るサンプルコードです。Reactでも同じコードが動きます。
import { useState } from 'hono/jsx'
import { render } from 'hono/jsx/dom'
function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
)
}
function App() {
return (
<html>
<body>
<Counter />
</body>
</html>
)
}
const root = document.getElementById('root')
render(<App />, root)
これを見るとReactでお馴染みのuseStateが使用されていますよね。
Honoでは useState や useEffect などの React互換のHooks が利用できます。
ただし、これらはReactの実装をそのまま使っているわけではなく、Hono独自のHooksです。
React互換フック
useState以外にもuseRefやuseEffect、useMemoなどフックが用意されています。
利用できるReact互換フック
useState()
useEffect()
useRef()
useCallback()
use()
startTransition()
useTransition()
useDeferredValue()
useMemo()
useLayoutEffect()
useReducer()
useDebugValue()
createElement()
memo()
isValidElement()
useId()
createRef()
forwardRef()
useImperativeHandle()
useSyncExternalStore()
useInsertionEffect()
useFormStatus()
useActionState()
useOptimistic()
hono/jsx/domをインポート
import { useState } from 'hono/jsx' // useStateをインポート
import { render } from 'hono/jsx/dom' // Honoでクライアントサイドできるようにする
Honoでは hono/jsx/domのrenderをインポートすることでそのコンポーネントはクライアントサイドレンダリングになります。
render()は内部で次のような処理をしています。
- JSXを解析
- NodeObjectを生成
- イベントをバインド
- stateとNodeObjectを関連付け
- 更新時は差分計算を行い、必要なDOMだけ更新
React・Next.jsとHono・HonoXのSSRとClient Componentsの繋ぎ方の違い 🔌
React・Next.jsはサーバーとクライアントで同じ仮想DOM構造を共有します。生成されたHTMLと仮想DOMを紐付けることをハイドレーションと呼びます。
ハイドレーションは初期表示に実行コストがかかったり、状況によってはレイアウト崩れが発生する可能性があります。
それに対して Hono では、
- SSR は仮想DOMを使わない単純な HTML 文字列生成
- CSR は軽量な仮想DOM(NodeObject)による DOM 更新
という、非常にシンプルな役割分担になっています。
サーバーとクライアントで複雑な仮想DOM(Fiber)を共有・同期する仕組みを持たないため、ハイドレーションの処理も軽量です。
なお、本記事は Reactを否定するものではなく、「状態が少ないUIにおける別の選択肢」として HonoのCSRを紹介するものです。
Reactの再レンダリングとHonoの再レンダリング ⚡
Reactの再レンダリング
Reactを触っていると、親コンポーネントの状態変更によって子コンポーネントの再レンダリングが起こらないように意識することがあると思います。再レンダリングとはコンポーネント関数の再実行のことですが、その結果として、一度仮想DOMを評価し、差分だけが実DOMに反映されます。
Honoの再レンダリング
Honoでもコンポーネント関数の再実行を行い、軽量な仮想DOM(NodeObject)で差分計算を行います。
Reactと異なり機能を絞っているので軽量です。
ReactとHonoのuseState
ReactのuseStateとHonoのuseStateも同じような動作をしますが、内部では違った仕組みで状態を更新しています。
setState()
│
▼
コンポーネント関数 再実行
│
▼
JSX を評価
│
▼
Fiber(仮想DOM)を生成
│
▼
Fiber(旧)と差分比較
│ ※ Fiberスケジューラによる複雑な最適化処理
▼
必要な差分だけ
実DOMに反映
👉 ポイントは
「高度な最適化機能を備えた差分計算」
という点です。
setState()
│
▼
コンポーネント関数 再実行
│
▼
JSX を評価
│
▼
NodeObject(軽量仮想DOM)を生成
│
▼
NodeObject(旧)と差分比較
│ ※ シンプルなkey/tag比較のみ
▼
必要な差分だけ
実DOMに反映
👉 ポイントは
「シンプルで軽量な差分計算」
という点です。
どちらも仮想DOMによる差分計算を行いますが、
Reactは複雑な最適化機能を持ち、Honoはシンプルで軽量という違いがあります。
| 項目 | React | Hono |
|---|---|---|
| サーバーサイドの仮想DOM | ✅ あり | ❌ なし(HTML直接生成) |
| クライアントサイドの仮想DOM | ✅ Fiber | ✅ NodeObject(軽量版) |
| 差分計算 | ✅ 複雑 | ✅ シンプルなkey/tag比較 |
| バンドルサイズ | ~40KB | ~3KB未満 |
ReactはDOM全体を管理することが必要ですが、HonoはDOMを限定的にしか管理しません。これはシングルページアプリケーション(SPA)はReactが得意であり、Honoは苦手という部分にも繋がります。
Honoのクライアントサイドレンダリングは局所的なインタラクションを扱うのが基本になります。
Reactの状態管理は過剰で必要でない場合も多くあるかもしれませんが、アプリケーションのUI自体がステートに依存しているのであれば、HonoではなくReactで開発をするのが良いかもしれません。また、HonoにはHonoXというIslands Architectureを採用した基本はSSR、部分的にCSRができるメタフレームワークも用意されています。
当然JSXが使えるのでHonoXで開発を始めて、やはりNext.jsでないと厳しいなと思ったら切り替えることも他のフレームワークからの乗り換えに比べれば圧倒的に楽です。
HonoでReactライブラリは動くのか
Reactの互換フックが使えて、仮想DOMならReact Hook FormやTanstack Form、Jestなどは動くのかと気になるかと思います。
実際に動くのかなと軽く試した程度ですが、意外ですがReact Hook Formは動きましたが、Tanstack Formは動きませんでした。React Hook Formもとりあえず動いただけでちゃんと動くのかはわかりません。
React依存でなくてもHonoはViteでビルドされるため、ESMではないCommonJS依存のライブラリが内包されているとうまく動かなかったり、エラーになりそうです。
Honoはあくまでもサーバーサイドのフレームワーク
HonoはRuby on Railsのようにサーバーサイドのフレームワークで、ページの一部をクライアントサイドができる程度に考えておく方が良いです。
試して思ったことは複雑なことをするならNext.jsやReact Routerにするべきです。
色々なライブラリが今後Honoにも対応していくことを願います。
まとめ
HonoのクライアントサイドレンダリングについてReactと比較しながらまとめてみました。
Honoはサーバーサイドのフレームワークですが、クライアントサイドレンダリングは仮想DOMやフックスなどReactの要素を取り入れたものになっています。
まだまだ進化しているので、Honoのクライアントサイドレンダリングはこんな感じと言い切りにくいですが、いろんなものを柔軟に取り込んでいるHonoはおもしろいですね。
ありがとうございました。