Skip to main content
← All Articles · · 12 min read

Hono.js Complete Guide: Ultra-Fast Web Framework for Edge Runtimes 2026

Master Hono.js: the ultra-fast web framework for Cloudflare Workers, Bun, Deno, and Node.js. Routing, middleware, JSX, RPC client, and performance benchmarks.

honoweb-frameworkedge-runtimecloudflare-workersbuntypescriptjavascript

If you’ve been building APIs on Node.js with Express and wondering if there’s something faster—there is. Hono.js is a small, fast, and universally deployable web framework that runs everywhere: Cloudflare Workers, Bun, Deno, Node.js, AWS Lambda, and more. In 2026, it’s become the go-to choice for edge-deployed APIs.

This guide covers everything you need to go from zero to production with Hono.


What is Hono.js?

Hono (炎, meaning “flame” in Japanese) is a web framework built on the Web Standards API (Request/Response/fetch). Because it speaks the same language as the browser and edge runtimes natively, it doesn’t need adapters or polyfills—it just runs.

Key facts:

  • ~14KB bundle size (no dependencies)
  • ~1M requests/sec throughput on Cloudflare Workers in benchmarks
  • Identical API across all runtimes
  • TypeScript-first with full type inference
  • Built-in JSX renderer, RPC client, and validation

Installation

npm create hono@latest my-app
# Select "cloudflare-workers" template
cd my-app
npm install
npm run dev

Bun

bun create hono@latest my-app
# Select "bun" template
cd my-app
bun install
bun run dev

Node.js

npm create hono@latest my-app
# Select "nodejs" template
cd my-app
npm install
npm run dev

Deno

import { Hono } from 'npm:hono'

The same Hono import works everywhere. This is the key insight: learn once, deploy anywhere.


Basic Routing

Hono’s routing API is similar to Express but fully typed:

import { Hono } from 'hono'

const app = new Hono()

// Basic routes
app.get('/', (c) => c.text('Hello Hono!'))
app.post('/users', (c) => c.json({ created: true }))
app.put('/users/:id', (c) => c.json({ updated: c.req.param('id') }))
app.delete('/users/:id', (c) => c.json({ deleted: true }))

export default app

Route Parameters

app.get('/posts/:id/comments/:commentId', (c) => {
  const { id, commentId } = c.req.param()
  return c.json({ postId: id, commentId })
})

Query Parameters

app.get('/search', (c) => {
  const query = c.req.query('q')
  const page = c.req.query('page') ?? '1'
  return c.json({ query, page: parseInt(page) })
})

Route Groups

const api = new Hono().basePath('/api')

api.get('/users', (c) => c.json([]))
api.get('/users/:id', (c) => c.json({ id: c.req.param('id') }))

app.route('/', api)

Middleware

Middleware in Hono uses the same async (c, next) pattern. It runs before and after handlers.

Built-in Middleware

import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { cors } from 'hono/cors'
import { bearerAuth } from 'hono/bearer-auth'

const app = new Hono()

// Logging
app.use('*', logger())

// CORS
app.use('/api/*', cors({
  origin: 'https://myapp.com',
  allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
}))

// Bearer token auth
app.use('/admin/*', bearerAuth({ token: process.env.ADMIN_TOKEN! }))

Custom Middleware

// Timing middleware
app.use('*', async (c, next) => {
  const start = Date.now()
  await next()
  const ms = Date.now() - start
  c.header('X-Response-Time', `${ms}ms`)
})

// Auth middleware with context
app.use('/protected/*', async (c, next) => {
  const token = c.req.header('Authorization')?.replace('Bearer ', '')
  if (!token) return c.json({ error: 'Unauthorized' }, 401)

  // Attach user to context
  c.set('user', { id: '123', token })
  await next()
})

// Access in handler
app.get('/protected/profile', (c) => {
  const user = c.get('user')
  return c.json(user)
})

Error Handling

app.onError((err, c) => {
  console.error(err)
  return c.json({ error: err.message }, 500)
})

app.notFound((c) => c.json({ error: 'Not found' }, 404))

Request and Response Helpers

The context object c is Hono’s primary API surface:

app.post('/data', async (c) => {
  // Parse body
  const body = await c.req.json()       // JSON
  const form = await c.req.formData()   // Form data
  const text = await c.req.text()       // Raw text

  // Headers
  const auth = c.req.header('Authorization')
  const ua = c.req.header('User-Agent')

  // Cookies
  const session = getCookie(c, 'session')

  // Response types
  return c.json({ ok: true }, 201)         // JSON
  return c.text('Hello', 200)              // Plain text
  return c.html('<h1>Hello</h1>')          // HTML
  return c.redirect('/new-url')            // Redirect
  return c.stream(async (stream) => {      // Streaming
    await stream.write('chunk1')
    await stream.write('chunk2')
  })
})

Input Validation with Zod

Hono integrates with Zod via the @hono/zod-validator package:

npm install zod @hono/zod-validator
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'

const createUserSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
  age: z.number().min(18).optional(),
})

app.post(
  '/users',
  zValidator('json', createUserSchema),
  async (c) => {
    const user = c.req.valid('json') // Fully typed!
    // user.name, user.email are guaranteed valid
    return c.json({ created: user }, 201)
  }
)

Validation errors automatically return 400 with error details.


JSX Support

Hono has a built-in JSX renderer for server-side HTML generation—no React required:

/** @jsx jsx */
/** @jsxImportSource hono/jsx */
import { Hono } from 'hono'
import { html } from 'hono/html'

const app = new Hono()

const Layout = ({ children }: { children: any }) => html`
<!DOCTYPE html>
<html>
  <head><title>My App</title></head>
  <body>${children}</body>
</html>
`

app.get('/', (c) => {
  return c.html(
    <Layout>
      <h1>Hello from Hono JSX!</h1>
    </Layout>
  )
})

For full JSX with component files, configure your tsconfig.json:

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "hono/jsx"
  }
}

RPC Client (Type-Safe API Calls)

This is Hono’s killer feature: a type-safe client generated from your server routes.

Server (api.ts):

import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'

const app = new Hono()
  .get('/posts', (c) => c.json([{ id: 1, title: 'Hello' }]))
  .post(
    '/posts',
    zValidator('json', z.object({ title: z.string() })),
    async (c) => {
      const { title } = c.req.valid('json')
      return c.json({ id: 2, title }, 201)
    }
  )

export type AppType = typeof app
export default app

Client (client.ts):

import { hc } from 'hono/client'
import type { AppType } from './api'

const client = hc<AppType>('https://api.example.com')

// Fully typed!
const posts = await client.posts.$get()
const data = await posts.json() // type: { id: number, title: string }[]

const created = await client.posts.$post({
  json: { title: 'New Post' },
})

No code generation, no schemas to maintain—the types flow directly from your route definitions.


Deploying to Cloudflare Workers

Cloudflare Workers is where Hono shines brightest. Your app runs at the edge, within 50ms of every user globally.

// src/index.ts
import { Hono } from 'hono'

const app = new Hono<{ Bindings: Env }>()

app.get('/kv/:key', async (c) => {
  // Access KV, D1, R2, and other Cloudflare bindings
  const value = await c.env.MY_KV.get(c.req.param('key'))
  return c.json({ value })
})

app.post('/db', async (c) => {
  const body = await c.req.json()
  const result = await c.env.DB.prepare(
    'INSERT INTO items (name) VALUES (?)'
  ).bind(body.name).run()
  return c.json(result)
})

export default app

wrangler.toml:

name = "my-api"
main = "src/index.ts"
compatibility_date = "2026-01-01"

[[kv_namespaces]]
binding = "MY_KV"
id = "your-kv-namespace-id"

[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "your-d1-id"

Deploy:

npx wrangler deploy

Bun Deployment

import { Hono } from 'hono'
import { serve } from '@hono/node-server'

const app = new Hono()
app.get('/', (c) => c.text('Hello from Bun!'))

serve({ fetch: app.fetch, port: 3000 })
bun run src/index.ts

Performance Benchmarks

Hono consistently tops framework benchmarks for edge and server runtimes. Approximate request/sec on Bun:

FrameworkReq/sec
Hono~310,000
Elysia~295,000
Fastify~80,000
Express~25,000

On Cloudflare Workers, Hono handles over 1M req/sec in benchmarks because routing uses a RadixTree algorithm with zero regex overhead.

The performance gap with Express isn’t just raw speed—it’s cold start time. Hono’s ~14KB bundle initializes near-instantly, while Express with its dependency chain can add 100ms+ to cold starts on serverless platforms.


Advanced Patterns

App Factory Pattern

// Testable app factory
export function createApp(config: Config) {
  const app = new Hono()

  app.use('*', logger())
  app.use('*', cors({ origin: config.allowedOrigins }))

  app.route('/api/v1', createV1Routes(config))

  return app
}

// Test
const app = createApp({ allowedOrigins: ['http://localhost:3000'] })
const res = await app.request('/api/v1/users')

Testing with Hono’s Test Helpers

import { describe, it, expect } from 'bun:test'
import app from './app'

describe('Users API', () => {
  it('GET /users returns array', async () => {
    const res = await app.request('/users')
    expect(res.status).toBe(200)
    const body = await res.json()
    expect(Array.isArray(body)).toBe(true)
  })

  it('POST /users creates user', async () => {
    const res = await app.request('/users', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name: 'Test', email: '[email protected]' }),
    })
    expect(res.status).toBe(201)
  })
})

No mocking required—Hono’s request method accepts standard Request objects.


When to Use Hono

Great fit:

  • Cloudflare Workers / edge APIs
  • Bun or Deno servers
  • Microservices where bundle size matters
  • Projects needing type-safe API clients (Hono RPC)
  • Full-stack TypeScript projects

Consider alternatives:

  • Large monolithic apps with complex plugin ecosystems (consider Fastify)
  • Apps with heavy ORM integrations where Prisma’s Node.js dependency chain is fine
  • Teams deeply invested in NestJS’s dependency injection patterns

Key Takeaways

Hono.js is the web framework built for 2026. It runs everywhere the Web Standards API exists, delivers near-zero overhead routing, and the RPC client feature eliminates an entire category of type-safety problems in full-stack TypeScript apps.

For edge deployments especially, there’s no real competitor. If you’re building a new API or microservice in 2026, Hono should be your first evaluation.

Explore Hono tools on DevPlaybook:

Free Download

50 CLI Commands That Save Hours Every Week

The exact Git shortcuts, file search tricks, Docker commands, and productivity hacks that senior developers use daily. Compiled from 5+ years of shipping production code.

  • Git mastery (10 cmds)
  • File & search wizardry
  • Docker & DevOps tricks
  • Process & network tools
  • Productivity boosters
  • macOS & Linux install guide

No spam. 1 email/week max. Unsubscribe anytime.

Found this guide useful? Check out our free developer tools.