with one click
hono-inertia
// Use @hono/inertia on the server and @ts-76/inertia-hono-jsx on the client to get SPA-style interactivity without leaving the Hono/hono-jsx stack.
// Use @hono/inertia on the server and @ts-76/inertia-hono-jsx on the client to get SPA-style interactivity without leaving the Hono/hono-jsx stack.
When a project on Hono needs real React — for the React component ecosystem, SSR, or an SPA — drop hono/jsx and wire React in via vite-ssr-components.
Owner-only. Reviews honojs/hono PRs or local branches with two checks — tests (do they pass, is the approach right) and breaking change (public API, types, hc).
Anything Cloudflare (Workers, D1, R2, KV, Durable Objects, Queues). Detailed guidance lives in the official cloudflare/skills collection.
How to add or update a skill in yusukebe/skills from inside another working project. Owner-only — uses Yusuke's local gh CLI auth.
Hono is the default web framework for any project here. Detailed guidance lives in the yusukebe/hono-skill collection on GitHub.
yusukebe/rj is a tiny CLI that fetches a URL and prints the response metadata (status, headers, protocol, timing) as JSON. It does not print the body — use it alongside `curl`, not as a replacement.
| name | hono-inertia |
| title | Build SPA-feel apps with Inertia.js + Hono + hono/jsx |
| description | Use @hono/inertia on the server and @ts-76/inertia-hono-jsx on the client to get SPA-style interactivity without leaving the Hono/hono-jsx stack. |
| tags | ["hono","inertia","hono-jsx","ui","spa","cloudflare-workers"] |
| references | ["https://inertiajs.com/","https://github.com/yusukebe/hono-inertia-example","https://github.com/ts-76/inertia-hono-jsx","https://www.npmjs.com/package/@hono/inertia","https://github.com/honojs/starter/tree/main/templates/cloudflare-workers+vite"] |
| related | ["hono","cloudflare"] |
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 hono/jsx as the client, you get SPA interactivity while staying inside one TypeScript stack — no React.
Reach for this when server-rendered hono/jsx is no longer enough — that is, when you need client-side state, optimistic UI, or rich forms.
hono + @hono/inertia. The inertia({ rootView }) middleware adds c.render(name, props) for returning Inertia responses.@ts-76/inertia-hono-jsx. Provides createInertiaApp, <Link>, <Form>, <Head>, useForm, useHttp, etc., on top of @inertiajs/core and hono/jsx/dom.@cloudflare/vite-plugin when deploying to Workers) and vite-ssr-components for asset wiring. Start from the cloudflare-workers+vite Hono starter template — it has the Vite + Workers wiring already in place: https://github.com/honojs/starter/tree/main/templates/cloudflare-workers+vite.@hono/inertia/vite generates pages.gen.ts so PagePropsFor<Name> resolves to the props passed to c.render(Name, ...).yusukebe/hono-inertia-example is the canonical layout (Hono + Inertia on Cloudflare Workers). It is written in React, so do not copy its app/client.tsx and app/root-view.tsx verbatim — those need to be rewritten against @ts-76/inertia-hono-jsx and hono/jsx. The server side (app/server.ts, route shape, Vite config, wrangler.jsonc, validation with @hono/zod-validator) transfers as-is.
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) returns either a full HTML document (on the first request) or a JSON Inertia page object (on subsequent client navigations) — @hono/inertia looks at the X-Inertia request header to decide.
// 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 setup, the adapter mounts <App /> for you. It uses hydrateRoot (from hono/jsx/dom) when the root has data-server-rendered, and createRoot otherwise.
// 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'> pulls the prop type from pages.gen.ts, which is generated by @hono/inertia/vite from the server routes — so a mismatch between server and client props is a build error, not a runtime one.
jsxImportSource: "hono/jsx" in tsconfig.json.vite-ssr-components/hono (not /react) in root-view.tsx to inject the Vite client and asset tags.@cloudflare/vite-plugin so wrangler deploy ships the SSR worker and the client bundle together.@ts-76/inertia-hono-jsx is community-scoped and "partial" hono/jsx support per its README — browser rendering targets hono/jsx/dom, but server-side rendering fidelity is not React-grade. Verify SSR output for any non-trivial page.X-Inertia-Version matches your client bundle hash, or clients silently force a full reload on every navigation.@cloudflare/vite-plugin handles this), not via fetch-to-R2.