目次
- はじめに
- HonoXとは
- セットアップ
- ディレクトリの構造と役割
- レンダリングの仕組み
- React化する
- レンダラーを変更する
- ライブラリを使用する
- bunでCloudflare Pagesにデプロイする
- おまけ - モノリポにおける依存関係との仁義なき戦い
- 参考
HonoのフルスタックメタフレームワークであるHonoXにreact-rendererミドルウェアを適用して、ReactベースのUIフレームワークであるYamadaUIとReact Flowを使用してポートフォリオサイトを作成しました。
本記事では、作成したポートフォリオサイトをCloudflare Pagesにデプロイするまでの方法をまとめています。
saku's Portfolio - Home
saku's Portfolio
https://sakupi01.com/
(リポジトリ)
HonoXは、HonoとViteを組み合わせてできたHonoのメタフレームワークです。ファイルベースルーティングやIslandアーキテクチャによるClient ComponentとServer Componentの棲み分け、SSRを実現し、Next.jsのようなフルスタックWebフレームワークの書き心地を実現したHonoアプリを作成できます。
詳細は、以下のyusukebeさんの記事を参照ください。
HonoXについて
https://zenn.dev/yusukebe/articles/724940fa3f2450
Starter templateに倣って、以下のコマンドを実行し、x-basic
を選択します。(※今回はパッケージマネジャにbunを使用しています)
実行完了すると、以下のようなディレクトリ構成でHonoXアプリが作成されます。
GitHub - honojs/honox: HonoX - Hono based meta framework
HonoX - Hono based meta framework. Contribute to honojs/honox development by creating an account on GitHub.
https://github.com/honojs/honox?tab=readme-ov-file#project-structure
上記の構成とコメントからわかるように、ファイルベースのルーティングやError Boundaryの設置ができることがわかります。
このディレクトリでbun install
->bun run dev
を実行し、http://localhost:5173 にアクセスすると以下のように初期画面が表示されます。
HonoXアプリの初期画面
レンダーは_renderer.tsx
で設定されたレンダラーによって行なわれます。デフォルトの場合だと、hono/jsx-renderer
のレンダラーであることがわかります。
レンダラーの引き数や戻り値はglobal.d.ts
で定義されています。
このように、デフォルトの設定ではhono/jsx-renderer
のレンダラーが使用されており、アプリケーション全体を通してhono/jsx
コンポーネントが使用されています。
例えば、hono/jsx
からインポートされたuseState
を使用して、以下のように[route]/app/islands/counter.tsx
を実装できます。
しかし、今回はReactベースのUIライブラリを使用するため、hono/jsx
をレンダーするhono/jsx-renderer
は使用できません。ReactベースのUIライブラリを使用するためには、react
のJSX(ReactNode
)をレンダーするためのreact-dom/client
が必要です。
そこで、次のステップでは、ReactNode
のレンダラーを提供するreact-dom/client
にレンダラーを変更し、使用するJSXをreact
から提供されているReactNode
にします🏃🏻♀️
HonoXの面白い特徴として、レンダラーとしてHono純正のhono/jsx-renderer
以外の任意のレンダラーを使用できます。
今回は、HonoXでReactのレンダラーを用いることでReactベースのUIライブラリであるYamadaUIやReact Flowを使用可能にしていきます。
まず、HonoXでReactNodeをレンダリングするために必要なモジュールをインストールします。
./app/client.ts
ではクライアントでコンポーネントをレンダリングするためのレンダラーの生成を行なっています。
引数を渡さない場合のデフォルトcreateClient
だと、レンダラーはhono/jsx-renderer
、コンポーネントはhono/jsx
として生成されます。
今回は、レンダラーをreact-dom/client
、コンポーネントをreact
コンポーネントとして生成するように設定します。
このように設定すると以下のような型エラーが出ると思いますが、こちらはKnown Issueとして確認されており、後続のリリースで修正されると思われますので、現時点では黙認しておきます。
Known Type Error in the use of react-renderer
Type Error Occurs When Trying to Use React with createClient · Issue #87 · honojs/honox
What version of HonoX are you using? 0.1.4 What steps can reproduce the bug? I attempted to set up the React renderer as described in the README, but encountered a type error. Write ./app/client.ts...
https://github.com/honojs/honox/issues/87
次に、@hono/react-renderer
のレンダラーが受け取るpropsを定義します。今回はページごとにtitle
とdescription
をheadに設定したかったので以下のように定義しました。
最後に、Reactレンダラーを適用して完成です。
各rootではglobal.d.ts
で定義したpropsを渡して、以下のようにレンダリングを構成できます。
viteはデフォルトではすべての依存関係を外部化、つまり、開発中にバンドルは行なわずプログラムの配布前にのみ行なうということをします。これにより、ビルドの高速化しています。
しかし、ViteのSSRではリンクされた依存関係はHMRをするために外部化しない、つまりSSRではリンクされたパッケージをバンドルに含めてビルドします。(Vite - 外部 SSR)
しかし、外部化したい、つまりリンクされたパッケージでもバンドルに含めずビルドしたい場合にはssr.externals
に入れる必要があります。
具体的にssr.externals
に含めるパッケージとしては以下のようなものが考えられるでしょう。
1. ブラウザ環境でのみ使用されるパッケージ
クライアントサイドレンダリング用のライブラリは、サーバー側レンダリング時には不要だからです。
例えば、YamadaUIの場合は以下のようなエラーが出ます。
2. Node.js依存のパッケージ
Node.js依存のコードには、Node.js固有のAPI、グローバル変数、モジュールシステムなどが使われており、これらはブラウザ環境では存在しません。そのため、ViteがNode.js依存コードをそのままトランスパイルしようとするとエラーになります。
そのため、Node.js依存のパッケージは ssr.external
に含める必要があるでしょう。
これにより、Viteはそのパッケージをバンドル化せずに、Node.js実行環境から直接読み込むようになります。
ssr.external
に含めないと、逆にViteはそのパッケージをバンドル化しようとしてエラーとなってしまいます。
SSR オプション
次世代フロントエンドツール
https://ja.vitejs.dev/config/ssr-options#ssr-オプション
従って、vite.config.ts
のssr.external
を以下のように追加して、YamadaUIとReact Flowをクライアントサイドで使用できるようにします。
これで、HonoXでReactやReactに依存しているUIコンポーネントを使用できるようになりました🎉
YamadaUIやReact Flowは、各ライブラリの公式ドキュメントに従うことで使用できます。
Cloudflare Pagesのプロジェクトを作成し、GitHubと統合します。これにより、main
ブランチに変更があった際、Cloudflare Pagesへ自動デプロイされるようになります。
しかし、初期設定のまま、パッケージマネジャとしてbunを使用してCloudflare Pagesで依存関係をインストールすると以下の状態から進行しませんでした。
パッケージマネジャにbunを用いる場合は、現状以下の環境変数の設定が必要なようです。
Cloudflare Pagesでbunを使用する環境変数設定
Cloudflare Pages and Bun
Cloudflare Pages and Bun. GitHub Gist: instantly share code, notes, and snippets.
https://gist.github.com/Hebilicious/88e5a444f42b8dc09fb86dfa865c6ed3
上記の事情により、SKIP_INSTALL_DEPENDENCY=true
としているのでビルドコマンドにbun install
を含めます。
あとは、上記を含めたビルドコマンドを構成し、ビルド出力ディレクトリなどを設定したらCloudflare Pagesにデプロイされるはずです🚀
本当は2日前くらいにデプロイ&この記事を書いていて、「よーし、ツイートして終わり〜〜」と思っていたのですが、投稿時にog画像がいつものようにうまく表示されなくなっていました。
ブログアプリはVercelにデプロイしているので、Vercelのログを確認してみたところ、og画像生成のためのファイルにうまくアクセスできていないようでした。
OG画像にアクセスした時のServerless Functionsでのエラー
原因を調査したところ、./apps/blog
と./apps/me
間での依存関係に整合性が取れてなかったことが問題だとわかりました。
具体的には、./apps/me
を付け足しで作った際にインストールした@hono/react-renderer
の内部依存パッケージAが、別マイクロサービスである./apps/blog
で^x.y.z
としてインストールしていたパッケージAとバッティングして、元々./apps/blog
で動いていたパッケージAのバージョンが上書きされてしまったことが原因でした。
解決方法としては、npm list --depth=0 --prod
で実際に使用されているパッケージのバージョンを全て吐き出し、^
を外して、そのバージョンをexactインストールすることで事なきを得ました......
特にモノリポ開発では、範囲を持ったままパッケージをインストールすることはキケンということを再認識させられるいい機会でした🙇🏻
honojs/honox: HonoX - Hono based meta framework
フルスタック Web フレームワーク HonoX を使ってみる
Viteでの開発中のSSR対応の仕組み | 東京工業大学デジタル創作同好会traP
middleware/packages/react-renderer at main · honojs/middleware
yusukebe/honox-react-nested-islands at island-in-island
createRoot – React
HonoX+Cloudflare Pagesで静的ファイルを読み込む