TanStack Hono - A SSR Monolith with Tanstack Router and Hono

TanStack Hono - A SSR Monolith with Tanstack Router and Hono Hero Image

Preface:

I’ve been using TanStack Router and Hono for a while and appreciate how well their philosophies align: TanStack Router gives you a strongly typed, data‑aware routing core; Hono provides a tiny, fast, standards‑leaning HTTP layer that runs comfortably on Node or edge platforms.

This project explores combining them into a single SSR + CSR monolith for TypeScript: one process (in dev) that can server‑render React routes, hydrate on the client, and expose lightweight API/RPC endpoints without jumping to a heavier meta‑framework.

Rather than serving the router purely as an SPA (what I had previously done) or using Hono/JSX, I adapted the official TanStack Router SSR example—swapping Express for Hono and layering in a typed API example to illustrate a simple RPC style.

Repo: github.com/bskimball/tanstack-hono

Development uses the Hono dev server with Vite powering both the client bundle and the server entry, so hot reloading applies across routes, server handlers, and shared types.

Why Hono?

Hono is a minimalist web framework that emphasizes performance and simplicity. It has a small footprint, making it ideal for serverless and edge environments. Its middleware system is straightforward, allowing for easy composition of request handlers. With its middleware, it’s easy to have RPC, Zod validations, and an OpenAPI compatible REST API.

Why Tanstack Router?

TanStack Router is a powerful routing library that provides strong typing and data awareness. It allows for nested routes, route loaders, and seamless integration with React. Its focus on type safety helps catch errors at compile time, making it a great fit for TypeScript projects. Additionally, TanStack Router’s flexibility allows it to be used in various environments, making it a versatile choice for building web applications.

Why a “Monolith”?

Monolith here simply means: one repository + one runtime process (in dev) that handles:

  • HTTP routing (API + SSR HTML responses)
  • React server rendering + hydration delivery
  • Static asset compilation (Vite)
  • Type‑safe internal RPC style endpoints

The goal is to keep operational complexity low while still enabling progressive enhancement and dynamic UI.


Architecture Overview

High level flow:

  1. Request enters Hono.
  2. Path is checked against API / RPC routes (e.g. /api/todos). If matched: handler runs, JSON returned.
  3. Otherwise SSR handler:
    • Creates a TanStack Router instance with the route tree
    • Resolves the matching route elements (no framework‑specific loader layer)
    • Renders React to string / stream
    • Injects serialized router state + asset tags
  4. Client hydrates the router instance; subsequent navigation is client‑side.

This pattern mirrors the official TanStack Router SSR example, replacing Express with Hono and integrating its middleware ergonomics + performance.


File / Responsibility Layout (Conceptual)

src/           # createRouter(), renderToString(), HTML template
  components/   # Shared React components
    Header.tsx      # Example shared header
	routes/
		__root.tsx         # Root route with layout + <Outlet />
		index.tsx          # / home route
		about.tsx          # /about
		todos/
			index.tsx        # /todos list
			$id.tsx          # /todos/:id detail
	api/
		todos.ts           # Example Hono route or RPC style handler
	shared/
		types.ts           # Shared Zod / TS types for API + UI
  entry-client.tsx   # Hydration entry
  entry-server.tsx   # Server render entry (invoked by ssr.ts)