Hono: The Ultrafast Framework for Every JavaScript Runtime

The name "Hono" means "flame" in Japanese, and it lives up to the name. Hono is an ultralight web framework that is built for the modern JavaScript runtime landscape, where your code might run on Node.js today, Cloudflare Workers tomorrow, and Bun next week. I picked it up earlier this year and it has become my go-to for any API or lightweight server project.
Why Hono Exists
Express has been the default Node.js web framework for over a decade. It works. But it has baggage. It was designed for a world where Node.js was the only JavaScript runtime and server-side JavaScript was a novelty. It is slow compared to modern alternatives, its TypeScript support is bolted on rather than built in, and it cannot run on edge runtimes like Cloudflare Workers or Deno Deploy without shims and workarounds.
Hono was built from scratch with a different set of assumptions. JavaScript runs everywhere now. TypeScript is the default, not an afterthought. Edge computing is mainstream. The framework should be tiny (under 14KB), fast (matching or beating Fastify in benchmarks), and portable across every runtime that supports the Web Standards API.
Multi-Runtime, For Real
This is the headline feature and it actually delivers. The same Hono application runs on:
- Node.js (via @hono/node-server)
- Deno
- Bun
- Cloudflare Workers
- Vercel Edge Functions
- AWS Lambda
- Fastly Compute
That is not a marketing claim I am repeating. I have personally deployed the same Hono app to Node.js locally, Cloudflare Workers in production, and Bun for testing. The code did not change. Only the entry point adapter changed. Your routes, middleware, and business logic are identical across runtimes.
This works because Hono builds on the Web Standards Request and Response APIs. Every modern JavaScript runtime supports these. Hono uses them as the foundation, so there is no runtime-specific abstraction layer to maintain.
TypeScript-First with Typed Routes
Hono's TypeScript integration goes beyond "we have type definitions." Routes are fully typed, including path parameters, query strings, and request bodies. Here is a simple example:
import { Hono } from "hono";
import { zValidator } from "@hono/zod-validator";
import { z } from "zod";
const app = new Hono();
const createUserSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
});
app.post(
"/users",
zValidator("json", createUserSchema),
(c) => {
const user = c.req.valid("json");
// user is fully typed: { name: string; email: string }
return c.json({ id: 1, ...user }, 201);
}
);
app.get("/users/:id", (c) => {
const id = c.req.param("id");
// id is typed as string
return c.json({ id, name: "Bryan" });
});
The `zValidator` middleware integrates Zod validation directly into the route handler chain. If validation fails, it returns a 400 response automatically. If it passes, the validated data is fully typed in your handler. No manual type assertions or casting needed.
The RPC Mode
This is where Hono gets really interesting. RPC mode gives you end-to-end type safety between your server and client, similar to what tRPC offers but without the extra abstraction layer.
// server.ts
const routes = app
.get("/api/posts", (c) => {
return c.json({ posts: [{ id: 1, title: "Hello" }] });
})
.post("/api/posts", zValidator("json", postSchema), (c) => {
const post = c.req.valid("json");
return c.json({ post }, 201);
});
export type AppType = typeof routes;
// client.ts
import { hc } from "hono/client";
import type { AppType } from "./server";
const client = hc<AppType>("http://localhost:3000");
const res = await client.api.posts.$get();
const data = await res.json();
// data is typed as { posts: { id: number; title: string }[] }
The client automatically knows every route, its parameters, and its response types. Change a route on the server, and TypeScript errors appear in the client immediately. No code generation step. No schema syncing. It just works through TypeScript's type inference.
Middleware System
Hono's middleware follows the same pattern as Express but with better typing and composition. Built-in middleware covers the common cases:
import { cors } from "hono/cors";
import { logger } from "hono/logger";
import { bearerAuth } from "hono/bearer-auth";
import { compress } from "hono/compress";
app.use("*", logger());
app.use("*", cors());
app.use("/api/*", bearerAuth({ token: "my-secret" }));
app.use("*", compress());
Custom middleware is straightforward. The `c.next()` pattern is familiar if you have used Express or Koa:
app.use("*", async (c, next) => {
const start = Date.now();
await next();
const duration = Date.now() - start;
c.header("X-Response-Time", String(duration) + "ms");
});
JSX for Server-Rendered HTML
Hono includes built-in JSX support for server-side rendering. Not React JSX. Hono's own JSX runtime that outputs HTML strings. This is perfect for simple pages, email templates, or lightweight server-rendered applications where you do not need a full React setup.
Benchmarks: Hono vs Express vs Fastify
I ran some informal benchmarks on my M2 MacBook Pro using autocannon with 100 concurrent connections over 10 seconds for a simple JSON response:
- Express: roughly 15,000 requests per second
- Fastify: roughly 55,000 requests per second
- Hono (Bun): roughly 130,000 requests per second
- Hono (Node.js): roughly 60,000 requests per second
Hono on Bun is absurdly fast. Even on Node.js, it matches or beats Fastify while offering a better developer experience and multi-runtime portability. On Cloudflare Workers, cold start times are negligible because the bundle is so small.
When to Choose Hono Over Express or Fastify
Choose Hono when you need multi-runtime deployment. If your app might run on Cloudflare Workers, Deno, or Bun, Hono is the obvious choice. Express cannot run on these runtimes without heavy shimming. Fastify is Node-only.
Choose Hono when you want TypeScript-first design. If you are tired of bolting type definitions onto Express, Hono's native TypeScript experience is a breath of fresh air.
Choose Hono when bundle size matters. For edge deployments where you pay per millisecond of execution time and cold starts matter, Hono's sub-14KB footprint is a real advantage.
Stick with Express if you need a massive ecosystem of battle-tested middleware. Express has middleware for everything imaginable. Hono's ecosystem is growing fast but is not there yet. Stick with Fastify if you are committed to Node.js and need the most mature, production-hardened performance-focused framework.
The "Write Once, Deploy Anywhere" Reality
I was skeptical of this promise. Every framework that claims portability ends up with runtime-specific workarounds and "works on X but not Y" gotchas.
Hono mostly delivers. The core routing and middleware work identically everywhere. The edge cases are around runtime-specific APIs: file system access on Cloudflare Workers (you do not have it), WebSocket handling differences between Bun and Node.js, and environment variable access patterns.
But the application code, your routes, your validation logic, your business logic, is genuinely portable. I have a REST API that I develop locally on Bun, test in CI on Node.js, and deploy to Cloudflare Workers. The only file that differs is the entry point, which is about 5 lines of adapter code.
For a framework that launched in late 2022, Hono has matured remarkably fast. It has earned its spot as my default choice for new API projects, and I do not see that changing anytime soon.