关于 hono-inertia
面向 TypeScript 开发者的 Hono、Inertia.js 与 hono/jsx 单栈 SPA 构建指南,无需单独编写前端或 JSON API 层。它还介绍页面属性类型生成,以及 Vite 和 Cloudflare Workers 部署要点。
主要功能
- 01使用 Hono 返回 Inertia 页面
- 02以 hono/jsx 构建客户端页面
- 03生成页面属性类型
- 04通过 Vite 打包和 SSR
- 05配置 Cloudflare Workers 部署
适用场景
- 01为 Hono 应用添加客户端状态
- 02构建乐观 UI 和复杂表单
- 03避免为 SPA 单独维护前端栈
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ yusukebe/ skills --skill hono-inertia安装后在 Claude Code 中输入/hono-inertia确认已加载。
详细介绍
使用 Inertia.js + Hono + hono/jsx 构建 SPA 体验的应用
Inertia.js 让你无需编写单独的前端或 JSON API 层,即可构建单页应用。服务端返回 Inertia 响应(页面名称 + 属性),轻量客户端适配器会即时切换当前页面组件。使用 Hono 作为服务端、hono/jsx 作为客户端,你可以在同一套 TypeScript 技术栈中获得 SPA 的交互体验,而且无需使用 React。
当服务端渲染的 hono/jsx 已无法满足需求时,可以考虑这种方案——也就是说,当你需要客户端状态、乐观 UI 或丰富的表单时。
技术栈
- 服务端:
hono+@hono/inertia。inertia({ rootView })中间件会添加c.render(name, props),用于返回 Inertia 响应。 - 客户端:
@ts-76/inertia-hono-jsx。它基于@inertiajs/core和hono/jsx/dom提供createInertiaApp、<Link>、<Form>、<Head>、useForm、useHttp等功能。 - 打包 / SSR: Vite(部署到 Workers 时使用
@cloudflare/vite-plugin)和vite-ssr-components,用于配置资源。可从cloudflare-workers+viteHono starter template 开始——它已配置好 Vite + Workers:https://github.com/honojs/starter/tree/main/templates/cloudflare-workers+vite。 - 页面类型:
@hono/inertia/vite会生成pages.gen.ts,使PagePropsFor<Name>能解析为传给c.render(Name, ...)的属性类型。
参考示例
yusukebe/hono-inertia-example 是标准的项目布局(在 Cloudflare Workers 上使用 Hono + Inertia)。它使用 React 编写,因此不要原样复制其中的 app/client.tsx 和 app/root-view.tsx——需要改用 @ts-76/inertia-hono-jsx 和 hono/jsx 重写。服务端部分(app/server.ts、路由结构、Vite 配置、wrangler.jsonc、使用 @hono/zod-validator 进行验证)则可直接沿用。
https://github.com/yusukebe/hono-inertia-example
服务端示例
// app/server.ts
import { Hono } from 'hono'
import { inertia } from '@hono/inertia'
import { rootView } from './root-view'
const app = new Hono()
app.use(inertia({ rootView }))
const routes = app
.get('/', (c) => c.render('Home', { message: 'Hono x Inertia' }))
.get('/users', (c) => c.render('Users/Index', { users: listUsers() }))
.get('/users/:id{[0-9]+}', (c) => {
const id = Number(c.req.param('id'))
const user = findUser(id)
if (!user) return c.notFound()
return c.render('Users/Show', { user })
})
export default routes
c.render(name, props) 返回完整的 HTML 文档(首次请求时)或 JSON Inertia 页面对象(后续客户端导航时)——@hono/inertia 会检查 X-Inertia 请求标头来决定返回哪种内容。
客户端示例(hono/jsx)
// app/client.tsx
import { createInertiaApp } from '@ts-76/inertia-hono-jsx'
createInertiaApp({
resolve: (name) => {
const pages = import.meta.glob('./pages/**/*.tsx', { eager: true })
return pages[`./pages/${name}.tsx`]
},
})
如果不设置自定义 setup,适配器会为你挂载 <App />。当根元素带有 data-server-rendered 时,它会使用 hydrateRoot(来自 hono/jsx/dom),否则使用 createRoot。
页面组件(hono/jsx)
// app/pages/Users/Index.tsx
import { Head, Link, type PageComponent } from '@ts-76/inertia-hono-jsx'
const UsersIndex: PageComponent<'Users/Index'> = ({ users }) => (
<main>
<Head title='Users' />
<h1>Users</h1>
{users.map((u) => (
<Link href={`/users/${u.id}`} key={u.id}>
{u.name}
</Link>
))}
</main>
)
export default UsersIndex
PageComponent<'Users/Index'> 会从 pages.gen.ts 获取属性类型。该文件由 @hono/inertia/vite 根据服务端路由生成,因此服务端与客户端的属性不匹配会导致构建错误,而非运行时错误。
默认设置
- 在
tsconfig.json中设置jsxImportSource: "hono/jsx"。 - 在
root-view.tsx中使用vite-ssr-components/hono(而非/react)来注入 Vite 客户端和资源标签。 - 使用
@cloudflare/vite-plugin部署,这样wrangler deploy就会一并发布 SSR Worker 和客户端包。
注意事项
@ts-76/inertia-hono-jsx属于社区维护,且根据其 README,对hono/jsx的支持是“部分的”——浏览器渲染针对hono/jsx/dom,但服务端渲染的还原度达不到 React 的水平。对于任何较复杂的页面,都应验证 SSR 输出。- Inertia 不是 API 框架。如果非 Inertia 客户端(移动端、第三方客户端)需要相同的数据,请构建单独的 JSON 端点——不要尝试复用 Inertia 响应结构。
- 资源版本控制:确保
X-Inertia-Version与客户端包哈希匹配,否则客户端会在每次导航时静默强制完整重新加载。 - 在 Cloudflare Workers 上,通过 Workers Assets 提供客户端包(
@cloudflare/vite-plugin会处理此事),不要通过 fetch 从 R2 获取。
深入了解
- Inertia 协议本身——篇幅短,值得通读:https://inertiajs.com/。
- hono/jsx 适配器 API 和类型模型:https://github.com/ts-76/inertia-hono-jsx。
- 可供参考的示例布局:https://github.com/yusukebe/hono-inertia-example。