hono-inertia
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseBuild SPA-feel apps with Inertia.js + Hono + hono/jsx
基于Inertia.js + Hono + hono/jsx构建类SPA应用
Inertia.js lets you build single-page apps without writing a separate frontend or a JSON API layer. The server returns Inertia responses (page name + props), and a thin client adapter swaps the current page component on the fly. With Hono as the server and as the client, you get SPA interactivity while staying inside one TypeScript stack — no React.
hono/jsxReach for this when server-rendered is no longer enough — that is, when you need client-side state, optimistic UI, or rich forms.
hono/jsxInertia.js 允许你构建单页应用,无需编写独立的前端或JSON API层。服务端返回Inertia响应(页面名称 + 属性),轻量的客户端适配器会动态替换当前页面组件。以Hono作为服务端、作为客户端,你可以在单一TypeScript技术栈内实现SPA交互——无需使用React。
hono/jsx当服务端渲染的无法满足需求时,就可以采用这套方案——比如当你需要客户端状态、乐观UI或复杂表单时。
hono/jsxStack
技术栈
- Server: +
hono. The@hono/inertiamiddleware addsinertia({ rootView })for returning Inertia responses.c.render(name, props) - Client: . Provides
@ts-76/inertia-hono-jsx,createInertiaApp,<Link>,<Form>,<Head>,useForm, etc., on top ofuseHttpand@inertiajs/core.hono/jsx/dom - Bundling / SSR: Vite (with when deploying to Workers) and
@cloudflare/vite-pluginfor asset wiring. Start from thevite-ssr-componentsHono starter template — it has the Vite + Workers wiring already in place: https://github.com/honojs/starter/tree/main/templates/cloudflare-workers+vite.cloudflare-workers+vite - Page typing: generates
@hono/inertia/vitesopages.gen.tsresolves to the props passed toPagePropsFor<Name>.c.render(Name, ...)
- 服务端: +
hono。@hono/inertia中间件新增了inertia({ rootView })方法,用于返回Inertia响应。c.render(name, props) - 客户端: 。在
@ts-76/inertia-hono-jsx和@inertiajs/core基础上提供了hono/jsx/dom、createInertiaApp、<Link>、<Form>、<Head>、useForm等功能。useHttp - 打包/SSR: Vite(部署到Workers时搭配)和
@cloudflare/vite-plugin用于资源关联。可以从vite-ssr-componentsHono启动模板开始——它已经配置好了Vite + Workers的关联:https://github.com/honojs/starter/tree/main/templates/cloudflare-workers+vite。cloudflare-workers+vite - 页面类型定义: 会生成
@hono/inertia/vite,因此pages.gen.ts可以解析为传递给PagePropsFor<Name>的属性类型。c.render(Name, ...)
Reference example
参考示例
yusukebe/hono-inertia-exampleapp/client.tsxapp/root-view.tsx@ts-76/inertia-hono-jsxhono/jsxapp/server.tswrangler.jsonc@hono/zod-validatoryusukebe/hono-inertia-exampleapp/client.tsxapp/root-view.tsx@ts-76/inertia-hono-jsxhono/jsxapp/server.tswrangler.jsonc@hono/zod-validatorServer sketch
服务端示例代码
ts
// 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 routesc.render(name, props)@hono/inertiaX-Inertiats
// 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 routesc.render(name, props)@hono/inertiaX-InertiaClient sketch (hono/jsx)
客户端示例代码(hono/jsx)
tsx
// 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`]
},
})Without a custom , the adapter mounts for you. It uses (from ) when the root has , and otherwise.
setup<App />hydrateRoothono/jsx/domdata-server-renderedcreateRoottsx
// 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-renderedhydrateRoothono/jsx/domcreateRootPage component (hono/jsx)
页面组件(hono/jsx)
tsx
// 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 UsersIndexPageComponent<'Users/Index'>pages.gen.ts@hono/inertia/vitetsx
// 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 UsersIndexPageComponent<'Users/Index'>pages.gen.ts@hono/inertia/viteDefaults
默认配置
- Set in
jsxImportSource: "hono/jsx".tsconfig.json - Use (not
vite-ssr-components/hono) in/reactto inject the Vite client and asset tags.root-view.tsx - Deploy with so
@cloudflare/vite-pluginships the SSR worker and the client bundle together.wrangler deploy
- 在中设置
tsconfig.json。jsxImportSource: "hono/jsx" - 在中使用
root-view.tsx(而非vite-ssr-components/hono)来注入Vite客户端和资源标签。/react - 使用进行部署,这样
@cloudflare/vite-plugin会将SSR Worker和客户端包一起发布。wrangler deploy
Pitfalls
注意事项
- is community-scoped and "partial"
@ts-76/inertia-hono-jsxsupport per its README — browser rendering targetshono/jsx, but server-side rendering fidelity is not React-grade. Verify SSR output for any non-trivial page.hono/jsx/dom - Inertia is not an API framework. If a non-Inertia client (mobile, third-party) needs the same data, build a separate JSON endpoint — do not try to reuse the Inertia response shape.
- Asset versioning: make sure matches your client bundle hash, or clients silently force a full reload on every navigation.
X-Inertia-Version - On Cloudflare Workers, serve the client bundle via Workers Assets (handles this), not via fetch-to-R2.
@cloudflare/vite-plugin
- 是社区维护的库,根据其README说明,它对
@ts-76/inertia-hono-jsx的支持是“部分”的——浏览器渲染基于hono/jsx,但服务端渲染的保真度无法达到React级别。对于任何非简单页面,都需要验证SSR输出。hono/jsx/dom - Inertia 不是 API框架。如果非Inertia客户端(移动端、第三方)需要相同的数据,请构建独立的JSON端点——不要尝试复用Inertia响应格式。
- 资源版本控制:确保与客户端包哈希值匹配,否则客户端会在每次导航时强制刷新页面。
X-Inertia-Version - 在Cloudflare Workers上,通过Workers Assets提供客户端包(会处理此操作),不要通过fetch-to-R2的方式。
@cloudflare/vite-plugin
Going deeper
深入学习
- The Inertia protocol itself — short, worth reading in full: https://inertiajs.com/.
- The hono/jsx adapter API surface and typing model: https://github.com/ts-76/inertia-hono-jsx.
- The example layout to copy: https://github.com/yusukebe/hono-inertia-example.
- Inertia协议本身——内容简短,值得完整阅读:https://inertiajs.com/。
- hono/jsx适配器的API和类型模型:https://github.com/ts-76/inertia-hono-jsx。
- 可参考的架构示例:https://github.com/yusukebe/hono-inertia-example。