☃️

HonoXの選定理由と設定まわりハマりどころ4選

に公開

こんにちは。株式会社シータグCTOの @y_okady です。

この記事は Hono Advent Calendar 2025 6日目の記事です。メリークリスマス!

シータグでは、2025年11月より新たに開発中のサービスのフレームワークに HonoX を採用しました。この記事では、冒頭でHonoXの選定理由を簡単にご紹介しつつ、導入時の設定でハマったところを4つご紹介します。

HonoXの選定理由

新サービス開発に当たって、以下の4つを要件としてフレームワークの選定に臨みました。

  • 少人数のエンジニアでスピード感を持って開発できること
  • フロントエンドとバックエンドどちらもTypeScriptで開発できること
  • フロントエンドをReactで開発できること
  • バックエンドのフレームワークは軽量であること(Next.jsのような重厚なものは対象外)

1つ目と2つ目について、フロントエンドとバックエンドの開発を別々のエンジニアが担当するようなチーム体制では、フロントエンド(SPA)とバックエンド(APIサーバー)を分離するアーキテクチャが主流です。一方、少人数のフルスタックエンジニアがすべての領域を担当するようなケースでは、アーキテクチャ分離により開発スピードの低下を招く恐れがあります。

3つ目も踏まえて、アーキテクチャとしてはSSR + ハイドレーションが良さそうということになりました。また、アプリケーションを軽量に保つために、アイランドアーキテクチャ(SSR + 部分ハイドレーション)を利用できるフレームワークを選定することにしました。

https://docs.astro.build/ja/concepts/islands/

上記の記事を提供してくれている Astro など、アイランドアーキテクチャを利用できるフレームワークはいくつかあります。また、アイランドアーキテクチャと似た仕組みであるReact Server Component(RSC)を利用できるNext.js App Routerも選択肢の一つではありますが、今回は以下の理由でHonoXを採用することに決めました。

  • 軽量なフレームワークであるという4つ目の要件を満たす
  • Honoを利用したことがある
  • HonoやHonoXの広がり、盛り上がりを感じていて将来性に期待が持てる
  • 日本人の yusukebe さんが中心となって開発していて応援したい

HonoXの導入は、README にあるように npm create hono@latest コマンドで x-basic を選択するところから始めました。

設定まわりのハマりどころ4選

これまで利用したことのないフレームワークを始めて触る際、私はいつも軽く動作確認してからまずディレクトリ構成を見直します。開発が進んでファイルが増えてからディレクトリ構成を見直すのも大変ですので、未来の状態をある程度見据えた構成を最初に用意するようにしています。

理想のディレクトリ構成を実現できたら、将来技術的にハマりそうなところをプロトタイピングで先に潰しておきます。例えば、フレームワークのマニュアルには載っていないことをどうやって実現できるのか、Reactなどコアとなるような外部ライブラリを問題なく利用できるか、といったものです。

HonoXは新しいフレームワークですのでまだ情報は少なく、AIに聞くなど軽く調べてもわからないことがあれば定義ファイルを辿ったり、トライアンドエラーで色々書いてみたり、READMEやフレームワークのソースコードを見たりしながら手探りで開発を進めていきました。

以下に、軽く調べても簡単に解決方法が見つからなかったものを4つご紹介します。

islands ディレクトリを変更したい

README にあるように、HonoXではデフォルトで app/islands がハイドレーション対象ディレクトリとなります。

これを変更して、以下のようなディレクトリ構成にしたいと考えました。

.
└── app
    ├── client
    │   ├── components
    │   │   └── counter.tsx // island component
    │   └── lib
    ├── server
    │   ├── components
    │   └── lib
    └── routes

これを実現するにはハイドレーション対象ディレクトリを app/client/components に変更する必要があります。READMEには記載がないものの、フレームワークのソースコードや定義ファイルを参考にして、以下のように設定できることがわかりました。

vite.config.js
import { defineConfig } from 'vite';
import honox from 'honox/vite';

export default defineConfig({
  plugins: [honox({
    islandComponents: { islandDir: "/app/client/components" },
  })],
})
app/client.ts
import { createClient } from "honox/client";
import type { ReactNode } from "react";

createClient({
  hydrate: async (elem, root) => {
    const { hydrateRoot } = await import("react-dom/client");
    hydrateRoot(root, elem as unknown as ReactNode);
  },
  createElement: async (type: any, props: any) => {
    const { createElement } = await import("react");
    return createElement(type, props) as unknown as Node;
  },
  ISLAND_FILES: { ...import.meta.glob("/app/client/components/**/*.tsx") },
});

ひとまずこれで正常に動作することは確認できましたが、createClient()ISLAND_FILES は裏技感が強くあまり望ましい設定ではない気もします。Issueを登録しておいたので今後の改善に期待!
https://github.com/honojs/honox/issues/344

アンダースコアを含むファイル名がハイドレーションの対象にならない

特に何も考えずにいつも通り user_menu.tsx のようなアンダースコアを含むファイル名にしたところ、ブラウザでJavaScriptが実行されず解決に時間がかかってしまいました。

後から気が付いたのですが、README に以下のようにちゃんと書かれていました。

It should be exported as a default or a proper component name that uses camel case but does not contain _ and is not all uppercase.

ハイドレーション対象ディレクトリにファイルを配置する場合は user-menu.tsx のような形式、それ以外のディレクトリに配置する場合は $user-menu.tsx_user-menu.island.tsx のような形式を利用できます。_*.island.tsx の先頭を除いて、いずれの形式もファイル名にアンダースコアを含む場合はハイドレーションの対象になりません。

[module|exports|require] is not defined エラーが発生する

外部ライブラリを npm install して利用すると、ライブラリによって module is not definedexports is not definedrequire is not defined エラーが発生する場合があります。

これらはViteのSSRで、CJS(CommonJS)形式のコードをESM(ES Modules)としてバンドルしようとして発生するエラーです。HonoXに限らずViteのSSRを利用する場合に遭遇することがあるのではないかと思います。

以下のように ssr.external にエラー発生の原因となる外部ライブラリを追加することで解消できます。

vite.config.js
import { defineConfig } from 'vite';

export default defineConfig({
  ssr: {
    external: ["react", "react-dom", "stripe"],
  },
})

アプリケーション側のCORS設定が正しくてもエラーが発生する

Honoの CORS Middleware を用いてアプリケーション側でCORSを正しく設定したにも関わらず、オリジンの異なるサイトからlocalhostに対してAPIリクエストを送るとCORSのエラーが発生してしまいました。過去にHonoアプリケーションで同様の処理を試した時は発生しませんでしたが、HonoXアプリケーションでは発生しました。

HonoとHonoXはViteを使っているかが大きな違いですので、その線で調査したところ上記のCORS Middlewareのドキュメントで以下の記載を見つけました。

When using Hono with Vite, you should disable Vite's built-in CORS feature by setting server.cors to false in your vite.config.ts. This prevents conflicts with Hono's CORS middleware.

以下のように設定することでViteのCORS機能を無効化し、HonoのCORS Middlewareで正しく処理できるようになりました。

vite.config.js
import { defineConfig } from 'vite';

export default defineConfig({
  server: {
    cors: false,
  },
})

おわりに

HonoXを触り始めて1ヶ月が経ちましたが、少人数のエンジニアでスピード感を持って新しいサービスを開発するのに適したフレームワークだと感じています。1日あたり10万リクエストまで無料で利用できるCloudflare Workersとの相性も抜群で、AWSなどのパブリッククラウドを利用する場合と比べてサービス運用コストを大幅に抑えることができます。

HonoやHonoXにはこれからも軽量であるという強みを維持しながら、利用者をどんどん増やしてより良いフレームワークに育っていってほしいなと思っています。この記事がHonoXの選定や導入のお役に立てれば幸いです。

株式会社シータグ

Discussion