テック技報最新号へ →
コード

Hono × Inertia(@hono/inertia)で API とクライアントルーターなしの React SPA を書く

Hono のルートで c.render('Posts/Index', { posts }) と返すだけで、app/pages/Posts/Index.tsx が props 付きで描画される。JSON 用の API、React Router、useEffect + fetch、取得データ用の useState が不要になり、ページ名と props の型もサーバーからクライアントまで通る。

@yusukebe · 投稿 2026-04-29
#typescript#hono#inertia#react#zod#frontend#cloudflare

書き方

tsx
// src/server.tsx(Hono)
app.get('/posts', (c) => c.render('Posts/Index', { posts: listPosts() }))
app.get('/posts/:id{[0-9]+}', (c) => {
  const post = findPost(Number(c.req.param('id')))
  if (!post) return c.notFound()
  return c.render('Posts/Show', { post })
})

// app/pages/Posts/Show.tsx(React)
export default function Show({ post }: PageProps<'Posts/Show'>) {
  return <article><h1>{post.title}</h1><p>{post.body}</p></article>
}
  • @hono/inertia/vite プラグインが app/pages/ を監視して pages.gen.ts を生成するので、c.render のページ名はファイルと一致しないと型エラーになり、補完も効く。
  • PageProps<'Posts/Show'> でサーバーが渡した props の型がそのまま届く。
  • 機能追加で触るのは「Hono のルート」と「app/pages/<Domain>/」の2か所だけ。

バリデーションは zod スキーマ1つから

ts
export const postInputSchema = z.object({
  title: z.string().min(1, 'タイトルは必須です').max(80, 'タイトルは80文字以内で入力してください'),
  body: z.string().min(1, '本文は必須です').max(2000),
})
export type PostInput = z.infer<typeof postInputSchema>   // ① 型

app.post('/posts',
  zValidator('json', postInputSchema, (result, c) => {     // ② 実行時チェック
    if (!result.success) {
      // 422 を返すのではなく、同じフォームを入力値とエラー付きで描き直す
      return c.render('Posts/New', {
        values: recoverInput(result.data),
        errors: toFieldErrors(result.error),               // ③ エラー文言がそのまま props に
      })
    }
  }),
  (c) => {
    const post = createPost(c.req.valid('json'))
    return c.redirect(`/posts/${post.id}`, 303)
  },
)

クライアントは res.status === 422 の分岐も state 復元も書かない。useForm(values) でサーバーが返した値から初期化すれば入力も消えない(Rails の render :new と同じ発想)。

向いているもの / 注意

  • CRUD 中心の管理画面や社内ツールなど「サーバーが画面遷移を決めてよい」アプリに向く。オフライン対応や複雑なクライアント状態が主役のアプリには別の構成が合う。
  • recoverInput / toFieldErrors は元記事のサンプル側のヘルパー。正確な API は @hono/inertia の README とサンプルリポジトリで確認する。
  • 試すなら github.com を bun install && bun run dev で動かすのが早い。

出典

X の元の投稿を開く