Skip to main content

Hono vs ElysiaJS vs Nitro (2026)

·PkgPulse Team
0

Hono runs on Cloudflare Workers, Deno, Bun, Node.js, and AWS Lambda — the same code, no modifications. ElysiaJS is 2.3x faster than Hono on Bun benchmarks and provides end-to-end type safety without code generation. Nitro powers Nuxt.js and is designed as the universal server layer for modern JavaScript frameworks. These aren't competing for the same use case — they're designed for different contexts.

TL;DR

Hono for edge-first, multi-runtime APIs where portability is the priority. ElysiaJS for Bun-first applications where you want the fastest TypeScript backend with end-to-end type safety. Nitro when you're building a framework, server for a meta-framework (Nuxt, Analog), or need a universal server adapter layer. For most new JavaScript API projects in 2026, Hono is the pragmatic default.

Key Takeaways

  • Hono: ~2M weekly npm downloads, 23K GitHub stars, runs on 9+ runtimes
  • ElysiaJS: ~300K weekly downloads, 11K GitHub stars, Bun-first, fastest TypeScript framework
  • Nitro: ~1.5M weekly downloads, 7K GitHub stars, Nuxt-backed, universal server layer
  • Hono bundle: <14 kB, Web Standards-based (Fetch API, Request, Response)
  • ElysiaJS Eden: end-to-end type safety without code generation (like tRPC but REST)
  • Nitro: H3 routing + Rollup bundling + auto-imports + Cloudflare/Lambda adapters
  • Express replacement context: all three are faster than Express

Why Not Express?

Express has 80M+ weekly downloads but was designed for Node.js in 2010. The issues in 2026:

  • No native TypeScript support (community types are imperfect)
  • Single-threaded, callback-based middleware
  • No edge runtime support (uses Node.js APIs)
  • No built-in type safety for request/response

These three frameworks are designed for the modern era: TypeScript-first, Web Standards-based, and edge-compatible.

Hono

Package: hono Weekly downloads: 2M GitHub stars: 23K Creator: Yusuke Wada

Hono is built on Web Standards — it uses the Fetch API (Request/Response) as its core abstraction. This means the same Hono application runs on any JavaScript runtime that supports those standards.

Supported Runtimes

Cloudflare Workers  ✓
Cloudflare Pages    ✓
Deno Deploy         ✓
Bun                 ✓
Vercel Edge         ✓
AWS Lambda          ✓
Node.js             ✓
Fastly Compute      ✓
Lagon               ✓

Installation

npm install hono

Basic Usage

import { Hono } from 'hono';

const app = new Hono();

app.get('/', (c) => c.text('Hello World!'));
app.get('/json', (c) => c.json({ message: 'Hello', status: 'ok' }));

// Path parameters
app.get('/users/:id', async (c) => {
  const id = c.req.param('id');
  const user = await getUserById(id);
  return c.json(user);
});

// POST with body parsing
app.post('/users', async (c) => {
  const body = await c.req.json<{ name: string; email: string }>();
  const user = await createUser(body);
  return c.json(user, 201);
});

export default app;

The export default app pattern works across all runtimes.

TypeScript with Zod Validation

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

const userSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
  age: z.number().int().positive(),
});

const app = new Hono();

app.post('/users', zValidator('json', userSchema), async (c) => {
  const data = c.req.valid('json'); // Fully typed: { name, email, age }
  const user = await createUser(data);
  return c.json(user, 201);
});

Hono RPC (Type-Safe Client)

// Server
import { Hono } from 'hono';

const app = new Hono()
  .get('/users/:id', (c) => c.json({ id: c.req.param('id'), name: 'Alice' }))
  .post('/users', zValidator('json', userSchema), (c) => c.json(c.req.valid('json')));

export type AppType = typeof app;

// Client (in a separate file)
import { hc } from 'hono/client';
import type { AppType } from './app';

const client = hc<AppType>('http://localhost:3000');

// Fully typed response:
const response = await client.users.$get();
const user = await response.json(); // TypeScript knows the shape

Middleware

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

app.use('*', logger());
app.use('/api/*', cors());
app.use('/api/admin/*', bearerAuth({ token: process.env.API_TOKEN }));

ElysiaJS

Package: elysia Weekly downloads: 300K GitHub stars: 11K Creator: Bogeychan, Saltyaom

ElysiaJS is designed specifically for Bun. While it runs on other runtimes, it's optimized for Bun's performance characteristics.

Installation

bun install elysia

Basic Usage

import { Elysia, t } from 'elysia';

const app = new Elysia()
  .get('/', () => 'Hello World!')
  .get('/json', () => ({ message: 'Hello', status: 'ok' }))
  .post('/users', ({ body }) => createUser(body), {
    body: t.Object({
      name: t.String({ minLength: 2 }),
      email: t.String({ format: 'email' }),
      age: t.Integer({ minimum: 0 }),
    }),
  })
  .listen(3000);

console.log(`Running at ${app.server?.hostname}:${app.server?.port}`);

Elysia uses its own TypeBox-based schema validation system (t), which is faster than Zod for runtime validation.

Eden Treaty: End-to-End Type Safety

Elysia's killer feature — type-safe client without code generation:

// server.ts
import { Elysia, t } from 'elysia';

const app = new Elysia()
  .get('/users/:id', ({ params }) => getUser(params.id))
  .post('/users', ({ body }) => createUser(body), {
    body: t.Object({ name: t.String(), email: t.String() }),
  });

export type App = typeof app;

// client.ts
import { treaty } from '@elysiajs/eden';
import type { App } from './server';

const client = treaty<App>('http://localhost:3000');

// TypeScript knows everything about the API:
const { data: user } = await client.users({ id: '123' }).get();
const { data: newUser } = await client.users.post({ name: 'Alice', email: 'alice@example.com' });

No schema duplication. No code generation. Types are inferred directly from the server definition.

Elysia Plugins

import { Elysia } from 'elysia';
import { swagger } from '@elysiajs/swagger';
import { cors } from '@elysiajs/cors';
import { jwt } from '@elysiajs/jwt';

const app = new Elysia()
  .use(swagger())  // Auto-generate Swagger docs
  .use(cors())
  .use(jwt({ secret: process.env.JWT_SECRET! }))
  .get('/protected', ({ jwt, headers }) => {
    const token = headers.authorization?.replace('Bearer ', '');
    const payload = jwt.verify(token);
    return payload;
  });

Performance

ElysiaJS on Bun benchmarks:

FrameworkRequests/sec (Bun)
ElysiaJS~280K
Hono~190K
Fastify (Node.js)~80K
Express (Node.js)~40K

Benchmarks from ElysiaJS's own comparison. Real-world results vary.

Nitro

Package: nitropack Weekly downloads: 1.5M GitHub stars: 7K Creator: UnJS team (Nuxt)

Nitro is different from Hono and Elysia — it's not primarily a framework for building APIs. It's a universal server layer for building frameworks and meta-frameworks. It powers Nuxt.js and is the foundation that frameworks build on.

What Nitro Provides

  • H3: The underlying HTTP framework (like Hono but from UnJS)
  • Auto-imports: Files in /api folder become API routes automatically
  • File-based routing: API routes via filesystem (/api/users/[id].ts)
  • Adapters: Deploy to Cloudflare, Vercel, AWS Lambda, Node.js with the same code
  • Bundling: Rollup-based bundling with tree-shaking

When to Use Nitro

// In a Nuxt 3 app, this is automatic:
// server/api/users/[id].ts
export default defineEventHandler(async (event) => {
  const id = getRouterParam(event, 'id');
  const user = await getUserById(id);
  return user; // Automatically serialized to JSON
});

Standalone Nitro

// nitro.config.ts
export default defineNitroConfig({
  routeRules: {
    '/api/**': { cors: true },
  },
  preset: 'cloudflare-workers', // Deploy target
});
// routes/api/users.ts
export default defineEventHandler(async (event) => {
  const users = await db.user.findMany();
  return users;
});
npx nitro dev
npx nitro build  # Outputs to .output/

H3: Nitro's Core HTTP Framework

H3 is the actual HTTP framework inside Nitro, similar to Hono but from the UnJS ecosystem:

import { createApp, defineEventHandler, getRouterParam, createRouter, readBody } from 'h3';

const app = createApp();
const router = createRouter();

router.get('/users/:id', defineEventHandler((event) => {
  const id = getRouterParam(event, 'id');
  return { id, name: 'Alice' };
}));

app.use(router);
export default app;

Performance Comparison

FrameworkRequests/sec (Node.js)Runtime SupportTypeScript Safety
ElysiaJS~280K (Bun)Bun-firstEnd-to-end (Eden)
Hono~120KUniversalRPC (hc)
H3/Nitro~90KUniversalGood
Fastify~80KNode.jsPlugins
Express~40KNode.jsTypes only

Feature Comparison

FeatureHonoElysiaJSNitro/H3
Runtime support9+ (universal)Bun-firstUniversal
Bundle size<14 kB~22 kBLarge
End-to-end typesRPC (hc)Eden (yes)No
Auto-importsNoNoYes
File-based routingNoNoYes
Plugin systemMiddlewareRichAdapters
Weekly downloads2M300K1.5M (Nuxt)
Best use caseEdge APIsBun APIsMeta-frameworks

Decision Guide

Choose Hono if:

  • Building an API that needs to run on edge runtimes (Cloudflare Workers)
  • Multi-runtime support is important
  • You want a simple, Express-like API with TypeScript
  • Building microservices or serverless functions
  • Portability across cloud providers is a requirement

Choose ElysiaJS if:

  • You're committed to Bun as your runtime
  • End-to-end type safety (Eden) is important
  • Maximum throughput on Bun is needed
  • You like the TypeBox validation approach
  • Building a full-stack TypeScript app with Bun

Choose Nitro/H3 if:

  • Building a meta-framework or universal server layer
  • You're in the Nuxt/UnJS ecosystem
  • File-based API routing is important
  • You need flexible deployment adapters as a primary concern

The 2026 Recommendation

For new API projects: Start with Hono. Its universal runtime support future-proofs you, and the TypeScript + Zod integration is excellent. If you switch to edge deployment later, no code changes needed.

For Bun-committed teams: ElysiaJS's performance and Eden type safety are compelling. The Bun ecosystem is maturing and the performance advantage is real.

For Nuxt/framework development: Nitro/H3 is the right abstraction layer — it's what you use when building for the ecosystem, not for end applications.

Middleware Ecosystems and Extension Points

One of the most practically important differences between these frameworks is how they handle middleware and what the available ecosystem looks like for common cross-cutting concerns: authentication, rate limiting, CORS, logging, and request validation.

Hono ships with a substantial built-in middleware library — hono/logger, hono/cors, hono/bearer-auth, hono/jwt, hono/rate-limiter, hono/compress, and more — all implemented using Web Standards APIs so they work on every runtime. Third-party middleware packages like @hono/zod-validator, @hono/sentry, and @hono/swagger-ui follow the same app.use() pattern. The middleware composition model is simple and explicit: middleware runs in the order it is registered, and each middleware function receives a Context object and a next function. This is deliberately similar to Express's middleware model, which lowers the learning curve for teams migrating from Express.

ElysiaJS takes a different approach with its plugin system. Plugins are more than middleware — they can extend the type system, add new methods to the Elysia instance, and register lifecycle hooks that run at specific stages of the request pipeline. The @elysiajs/swagger plugin, for example, doesn't just add a /docs route; it reads your route definitions and TypeBox schemas at registration time to generate a complete OpenAPI specification. This level of metaprogramming is possible because ElysiaJS's TypeScript type system tracks every route, its parameters, and its response types at compile time. The cost is that plugins must be written with an awareness of the type system, making third-party plugin development more complex than writing a simple Hono middleware function.

Nitro's middleware model is inherited from H3, which uses defineEventHandler and hooks registered on the Nitro application. The nitro.hooks API provides lifecycle hooks (request, error, close) that apply globally. Route-specific middleware is handled by placing files in a middleware/ directory where they apply to all matching routes. This convention-over-configuration approach reduces boilerplate but makes the request pipeline less explicit than Hono or Elysia's code-first middleware registration.

Deployment Bundles and Cold Start Characteristics

For serverless and edge deployment scenarios where cold start latency affects user-perceived performance, the deployment bundle characteristics of these frameworks matter as much as their raw throughput numbers.

Hono's sub-14KB bundle is genuinely small. A Cloudflare Worker that uses Hono for routing, Zod for validation, and a few business logic modules can realistically stay under 50KB compressed. Cloudflare Workers initialize these bundles in microseconds — the worker runtime is designed for sub-millisecond initialization. Hono's Web Standards-based API means no Node.js compatibility layer is needed, avoiding the significant overhead that running Node.js APIs on Cloudflare Workers adds.

ElysiaJS's optimization story is specific to Bun. Bun's runtime starts significantly faster than Node.js for the same application code, and ElysiaJS takes advantage of Bun-specific internals for performance. For AWS Lambda with Node.js runtime, ElysiaJS's performance advantage over Hono narrows substantially — the Bun-specific optimizations do not apply. Teams deploying to Bun on Fly.io, Railway, or similar platforms that support custom runtimes see the full performance benefit; teams deploying to standard Lambda or Cloud Run functions do not.

Nitro's deployment bundles are larger because Nitro includes the full H3 framework, the Rollup output bundle, and auto-import infrastructure. This is appropriate for framework and meta-framework use cases where the added capabilities justify the size. For a simple JSON API endpoint, Nitro's bundle size represents overhead that Hono avoids. The Nitro preset system does tree-shake aggressively for specific targets like Cloudflare Workers, bringing the bundle closer to a minimal Hono deployment.

Compare package download trends on PkgPulse.

Compare Hono, Elysiajs, and Nitro package health on PkgPulse.

See also: Fastify vs Hono and Express vs Hono, Hono vs ElysiaJS vs Nitro (2026).

The 2026 JavaScript Stack Cheatsheet

One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.