HonoXは、軽量WebフレームワークのHonoにViteを組み合わせ、ファイルベースルーティング・SSR・Islands(部分ハイドレーション)を足したメタフレームワークです。2026年10月時点の最新はv0.1.62(2026年10月8日公開)で、公式READMEは今も「alpha stage」と明記しています。本記事では、雛形を実際に生成・ビルドして測った値をもとに、始め方、ファイルベースルーティングの利点と注意点、Islandsで配られるJavaScriptの量、Cloudflare Workersへのデプロイ、テストの書き方、そして本番で採用してよい条件を整理します。
まとめ
- HonoXは「Honoのアプリ+Viteの設定」でできている。実行時の処理はHono、開発サーバーとビルドはViteが受け持つ。
- 最新はv0.1.62。0.1系の間も破壊的変更が入る方針なので、
package.jsonで版を固定して使う。create-honoの雛形も"honox": "0.1.61"と固定で入る。 - ルートは
app/routesのファイル配置で決まり、1ファイルにGETとPOSTを同居できる。-で始まるファイルはルートから外れるので、部品を同じ場所に置ける。 - 検証した雛形では、本番時に島を使うページだけにクライアントJSの読み込みタグが出る。島1個で読み込むJSのgzip圧縮サイズ合計は約14KBで、島のないページにはJSの読み込みタグがなかった。
- 向くのはエッジ配信の小〜中規模サイトや、少しのUIを持つAPI。長期運用で破壊的変更への追随が難しい場合は、画面要件に応じてNext.jsやAstroを比較する。
HonoXの位置づけとHono・Viteの役割分担
メタフレームワークとは、UIライブラリやWebフレームワークの上に、ルーティング・レンダリング・ビルドの決まりごとを足した上位のフレームワークです。React上のNext.js、Vue上のNuxtと同じ関係で、HonoXはHonoの上に乗ります。公式リポジトリは自らを「full-stack websites or Web APIs」を作るためのメタフレームワークと説明しており、前身はSonikというプロジェクトです。リポジトリの作成は2023年12月、ライセンスはMITです。
HonoとViteがそれぞれ受け持つ処理
「hono vite」で調べると両者の設定が混ざって見えますが、受け持つ時点がはっきり分かれています。
| 観点 | Hono | Vite |
|---|---|---|
| 受け持つ処理 | ルーティング・リクエスト処理・SSR | 開発サーバー・HMR・ビルド |
| 動く時点 | 実行時(Workers・Node.js等) | 開発時・ビルド時 |
| HonoXでの入口 | app/server.tsのcreateApp() |
vite.config.tsのhonox() |
| 雛形の版(実測) | hono ^4.13.14 | vite ^8.1.4 |
createApp()が返すのはHonoのインスタンスそのものです。そのためhono/loggerや@hono/zod-validatorなどHonoのミドルウェアは書き換えずに使えます。Hono本体の書き方や他フレームワークとの違いはHono vs FastAPI(Python)の違い|Honoとは・メリット・使い方を実行結果で比較【Hono 4.13】で扱っています。
HonoXが持たない機能
HonoXが足すのは、ファイルベースルーティング、SSR、Islands、クライアントエントリの4つです。データ取得の仕組み、状態管理、ORM、画像最適化は持ちません。描画もHono JSXが既定で、React・Preact・Solidなどへ差し替えられます(Bring Your Own Renderer)。Next.jsのような全部入りを期待すると、足りない機能をHonoのミドルウェアや外部ライブラリで埋める作業が発生します。
HonoXのインストールと雛形の起動
新規プロジェクトはcreate-honoで作り、テンプレートにx-basicを選びます。対話を省くなら--templateで指定できます。
npm create hono@latest my-app -- --template x-basic --pm npm
cd my-app
npm run dev
2026年10月28日にcreate-hono 0.19.5で生成した雛形の中身は次のとおりです。Tailwind CSS 4とCloudflare Workers向けの設定が最初から入っています。
app/
client.ts # クライアントエントリ(createClient)
global.d.ts # Bindings・Variables の型
islands/counter.tsx # 島の例
routes/
_404.tsx _error.tsx _renderer.tsx index.tsx
server.ts # サーバーエントリ(createApp)
style.css
public/
vite.config.ts
wrangler.jsonc
注意したいのは版の扱いです。生成されたpackage.jsonは"honox": "0.1.61"と固定で、npmの最新である0.1.62より1つ古い状態でした。0.1.62の変更はpeerDependenciesの範囲だけなので、そのままでも動きますが、0.1.62に更新して版を固定するならnpm i --save-exact [email protected]を実行します。またViteは8系が入るため、Node.jsはViteの要件である20.19以上または22.12以上が必要です。
ファイルベースルーティングの書き方とメリット・注意点
app/routesに置いたファイルのパスがそのままURLになります。routes/about.tsxは/about、routes/posts/[id].tsxは/posts/:idです。
ルートの3つの書き方(createRoute・Honoインスタンス・JSX)
READMEは3通りの書き方を示しています。画面はcreateRoute()、JSONを返すAPIはHonoインスタンス、静的な画面はJSXを返す関数、と使い分けると読みやすくなります。createRoute()ではdefault exportがGET、名前付きのPOST・PUT・DELETEが各メソッドに対応します。
// app/routes/index.tsx(画面:GETとPOSTを1ファイルに)
import { createRoute } from 'honox/factory'
export const POST = createRoute(async (c) => {
const { name } = await c.req.parseBody<{ name: string }>()
return c.redirect(`/?name=${encodeURIComponent(name)}`)
})
export default createRoute((c) => {
const name = c.req.query('name') ?? 'Hono'
return c.render(<h1>Hello, {name}!</h1>)
})
// app/routes/api/hello.ts(API:Honoインスタンスをexport)
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.json({ message: 'hello' }))
export default app
上のapi/hello.tsを置いた状態で/api/helloにリクエストすると、application/jsonで{"message":"hello"}が返ることを確認しました。
アンダースコアで始まる特別ファイルとネストレイアウト
アンダースコアで始まるファイルは、URLではなく役割を持ちます。
| ファイル | 役割 | 効く範囲 |
|---|---|---|
_renderer.tsx |
HTMLの外枠(レイアウト) | そのディレクトリ以下 |
_middleware.ts |
Honoのミドルウェアを並べる | そのディレクトリ以下 |
_404.tsx |
Not Foundページ | アプリ全体 |
_error.tsx |
エラーページ | アプリ全体 |
サブディレクトリに_renderer.tsxを置くとレイアウトが入れ子になります。子のレンダラーに渡したtitleなどのpropsは親へ自動では伝わらないため、子側で<Layout title={title}>のように明示して渡します。
// app/routes/posts/_renderer.tsx
import { jsxRenderer } from 'hono/jsx-renderer'
export default jsxRenderer(({ children, Layout, title }) => (
<Layout title={title}>
<nav>Posts Menu</nav>
<main>{children}</main>
</Layout>
))
// app/routes/_middleware.ts(全ルートに適用)
import { createRoute } from 'honox/factory'
import { logger } from 'hono/logger'
import { secureHeaders } from 'hono/secure-headers'
export default createRoute(logger(), secureHeaders())
ファイルベースルーティングのメリットと注意点
Hono単体でapp.get()を並べる書き方と比べたときの利点は次の4つです。
- URLとファイルが1対1:ルート定義を探し回らずに済み、ページ数が増えてもルーター設定のファイルが肥大化しない。
- メソッドの同居:フォームの表示(GET)と送信(POST)を同じファイルに書ける。
- ディレクトリ単位のミドルウェア:
routes/admin/_middleware.tsに認証を書けば、/admin以下だけに効く。 - 部品を同じ場所に置ける:
-で始まるファイルやディレクトリはルートから除外される。routes/-parts/header.tsxを置いて/-parts/headerを叩くと404になることを確認した。
注意点もあります。ルートの収集はViteの機能に依存するため、createApp()はVite(またはVitest)を通さないと動きません。末尾スラッシュは既定で取り除かれ、routes/docs/index.tsxは/docsになります。/docs/で配信したい既存サイトを移す場合はcreateApp({ trailingSlash: true })を指定します。
IslandsによるJavaScriptの部分配信
HonoXはサーバー側で描画したHTMLを返し、操作が必要な部品だけを島(Island)としてクライアントでハイドレーションします。考え方の背景はアイランドアーキテクチャとは?仕組みとAstro 7の実装・JS量の実測、PPRとの違いで詳しく扱っています。
島として認識される条件
READMEの条件は2つです。1つはapp/islandsディレクトリに置くか、$counter.tsxのように$で始まる名前にすること。もう1つは、default exportするか、_を含まず全部大文字でもないキャメルケースの名前でexportすることです。島の中ではHonoのContext(c)を参照できないので、リクエスト由来の値は外側のコンポーネントで取り出してpropsで渡します。
// app/islands/counter.tsx
import { useState } from 'hono/jsx'
export default function Counter({ init = 0 }: { init?: number }) {
const [count, setCount] = useState(init)
return <button onClick={() => setCount(count + 1)}>count: {count}</button>
}
// app/routes/counter.tsx(Contextの値はここで取り出して渡す)
import { createRoute } from 'honox/factory'
import Counter from '../islands/counter'
export default createRoute((c) =>
c.render(<Counter init={Number(c.req.query('count') ?? '0')} />)
)
島の有無で変わるJavaScriptの配信量(実測)
2026年10月28日にmacOS・Node.js 26.5.0・Vite 8.3.5・Hono 4.13.14で、雛形(島はカウンター1個)をHonoX 0.1.62でビルドし、wrangler devで配信して確かめました。雛形の_renderer.tsxが使う<Script>は、本番では<HasIslands>で包まれており、島を描画したページにだけscriptタグを出力します。
| ページ | scriptタグ | 対象JSのgzip圧縮サイズ(ビルド値) |
|---|---|---|
島ありの/ |
あり | client 11.12KB+counter 1.83KB+runtime 1.00KB |
島なしの/about |
なし | 0KB |
サーバー側のバンドルdist/index.jsは57.71KB(gzip 22.21KB)でした。記事ページのように操作のない画面が多いサイトほど、島だけにJSを絞る効果が大きくなります。なお開発サーバーでは島の有無に関係なく/app/client.tsが読み込まれるので、配信量は必ず本番ビルドで確認してください。
Reactで描画する場合(BYOR)
Hono JSXの代わりにReactを使うときは、@hono/react-renderer(1.0.1、React 19が必要)を入れて_renderer.tsxをreactRendererに替えます。あわせてapp/client.tsのcreateClient()にhydrateとcreateElementを渡してReactでハイドレーションさせ、tsconfig.jsonのjsxImportSourceをreactにします。クライアント用の出力先(static/client.js)もvite.config.tsで指定が要るため、手順はREADMEのReact caseをそのまま写すのが確実です。Reactに寄せるほどNext.jsとの差は小さくなるので、Reactが必須ならNext.jsも同時に検討します。
ビルドとCloudflare Workers・その他の環境へのデプロイ
ビルドはクライアント用とサーバー用の2回に分けて実行します。雛形のnpm run buildはvite build --mode client && vite buildで、手元ではクライアント側・サーバー側とも1.1秒以内で終わりました。Vite 8では「honox-vite-client」プラグインのesbuildオプションが非推奨だという警告が出ますが、ビルドは成功します。
Cloudflare Workersへのデプロイ
現行のx-basicはCloudflare Workersの静的アセットを使う構成です。wrangler.jsoncでサーバーの入口とアセットのディレクトリを指定し、npm run deploy(ビルド+wrangler deploy)で公開します。
// wrangler.jsonc(雛形の要点)
{
"main": "./dist/index.js",
"compatibility_flags": ["nodejs_compat"],
"assets": { "directory": "./dist" }
}
// vite.config.ts(雛形)
import build from '@hono/vite-build/cloudflare-workers'
import adapter from '@hono/vite-dev-server/cloudflare'
import tailwindcss from '@tailwindcss/vite'
import honox from 'honox/vite'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
honox({
devServer: { adapter },
client: { input: ['/app/client.ts', '/app/style.css'] }
}),
tailwindcss(),
build()
]
})
2024年ごろの解説記事には、@hono/vite-cloudflare-pagesで_worker.jsを出力するCloudflare Pages向けの手順が残っています。いま雛形から始めるならWorkers構成のままで構いません。Workers側の料金や無料枠はCloudflare Workersとは?料金・無料枠・Workers Paidの中身と対応言語・使い方にまとめています。
Node.js・Bunで動かす場合とSSG
HonoXはHonoが動く環境ならどこでも動きます。@hono/vite-buildのcloudflare-workersをbunやnodeに、@hono/vite-dev-serverのアダプターも同じ環境のものに替えます。サーバー上で起動するときは、cd ./distで出力ディレクトリに移ってから起動しないと静的アセットが見つかりません。
もう1つの落とし穴が環境変数です。Viteはビルド時にprocess.envを空のオブジェクトへ置き換えるため、開発中は読めたprocess.env.MY_VARが本番でundefinedになります。vite.config.tsにdefine: { 'process.env': 'process.env' }を足すと実行時の値を読めます。全ページを静的HTMLにしたい場合は、@hono/vite-ssg(0.3.3)をプラグインに追加します。
Vitestによるルートの結合テスト
READMEは、HonoXがViteの拡張に依存しているため、テストランナーにVitestを推奨しています。createApp()で作ったアプリにapp.request()でリクエストを送り、ステータスと本文を確かめる形です。
// tests/index.test.ts
import { describe, expect, it } from 'vitest'
import { createApp } from 'honox/server'
const app = createApp()
describe('routes', () => {
it('トップページがnameを表示する', async () => {
const res = await app.request('/?name=Hono')
expect(res.status).toBe(200)
expect(await res.text()).toContain('Hello, Hono!')
})
it('APIがJSONを返す', async () => {
const res = await app.request('/api/hello')
expect(await res.json()).toEqual({ message: 'hello' })
})
})
npm i -D vitestで入ったVitest 5.0.5でnpx vitest runを実行し、この形のテストが通ることを確認しました。vitest.config.tsを別に作るとそちらが優先されるので、mergeConfigでvite.config.tsの設定と合わせます。
v0.1.62時点の開発状況とHono v5への対応
HonoXは0.1系のまま更新が続いています。リリースを見ると、2026年5月の0.1.56以降は主に島まわりの修正です。
| 版 | 公開日 | 主な変更 |
|---|---|---|
| 0.1.56 | 2026-05-28 | ClientOptionsをJSX要素の型で汎用化 |
| 0.1.57・0.1.58 | 2026-07-01 | 57:依存走査修正/58:npm公開CI変更 |
| 0.1.60 | 2026-07-25 | 島の依存解決をimport元ごとに |
| 0.1.61 | 2026-08-18 | 入れ子の島のハイドレーション修正 |
| 0.1.62 | 2026-10-08 | Hono v5のプレリリースを許容 |
0.1.62はpeerDependenciesをhono: ">=4.* || ^5.0.0-0"に広げました。Hono本体はnpmで5.0.0-rc.1(2026年10月10日)がnextタグに出ており、latestは4.13.14です。雛形は4系を入れるため、本番では4系を維持し、v5への移行は正式版の公開後に互換性を検証してから判断します。
HonoXを採用すべき場面・避けるべき場面
判断の軸は「0.1系の破壊的変更に追随できるか」です。READMEはzeroverに従い、同じメジャー版の中でも破壊的変更を入れると明言しています。版を固定し、更新のたびにリリースノートとテストで確かめる運用を回せるなら、本番でも使えます。
向いているのは次のような案件です。
- ブログ・ドキュメント・LPなど、操作する部品が少なくHTML中心のサイト(検証した雛形では、島のないページにJSの読み込みタグを出力しない)。
- Cloudflare Workersでフロントとちょっとした管理画面・APIを1つにまとめたいもの。
- 既にHonoでAPIを書いていて、同じミドルウェアのまま画面を足したいチーム。
避けたほうがよいのは、破壊的変更への追随工数を確保できない長期運用の業務システム、画面の大半が状態を持つSPA、認証・画像最適化・ISRなどを既製品で揃えたい案件です。これらはNext.jsか、同じIslandsの考え方で実績の多いAstroを選ぶほうが安全です。逆に「APIに数画面を足すだけ」なら、HonoXを入れずHono単体にhono/jsx-rendererを足せば済むこともあります。Islandsと軽さを重視しつつDenoで動かしたいならDeno Freshも候補です。HonoXやCloudflare Workersを使った業務Webアプリの開発体制から相談したい場合は、業務用・Webアプリ開発のページもご覧ください。
よくある質問
HonoXとHonoの違いは何ですか?
Honoはルーターとミドルウェアを提供するWebフレームワークで、HonoXはその上にViteを組み合わせ、ファイルベースルーティング・SSR・Islands・クライアントエントリを足したメタフレームワークです。HonoXのcreateApp()はHonoのインスタンスを返すので、Honoのミドルウェアはそのまま使えます。
HonoXは本番環境で使えますか?
使えますが、v0.1.62の時点でも公式は「alpha stage」としており、0.1系の中でも破壊的変更が入ります。package.jsonで版を固定し、更新時にリリースノートとVitestの結合テストで確かめる運用が前提です。
HonoXでReactは使えますか?
使えます。既定はHono JSXですが、@hono/react-rendererで_renderer.tsxを差し替え、createClient()にhydrate: (elem, root) => { hydrateRoot(root, elem) }(ReactのhydrateRootは引数の順が逆なので入れ替える)とcreateElementを設定します。描画にはReact用のJSX設定とビルド設定も必要です。
HonoXの最新バージョンはいくつですか?
2026年10月28日時点のnpmの最新は0.1.62(2026年10月8日公開)です。ただしcreate-honoのx-basicは0.1.61を固定で入れるため、0.1.62に更新して版を固定するにはnpm i --save-exact [email protected]を実行します。導入前にnpmのhonoxで最新を確認してください。
メタフレームワークとは何ですか?
UIライブラリやWebフレームワークの上に、ルーティング・レンダリング・ビルドの決まりごとを足した上位のフレームワークのことです。Reactに対するNext.js、Vueに対するNuxtと同じ関係で、HonoXはHonoを土台にしたメタフレームワークです。