Skip to main content

Overview

Hono provides multiple router implementations, each optimized for different use cases. All routers implement the same Router interface defined in src/router.ts:29:
export interface Router<T> {
  name: string
  add(method: string, path: string, handler: T): void
  match(method: string, path: string): Result<T>
}

SmartRouter (Default)

The default router that automatically selects the best router implementation based on your routes.
import { Hono } from 'hono'

const app = new Hono() // Uses SmartRouter by default

How It Works

From src/router/smart-router/router.ts:21, SmartRouter tries each router until one succeeds:
match(method: string, path: string): Result<T> {
  const routers = this.#routers
  const routes = this.#routes

  const len = routers.length
  let i = 0
  let res
  for (; i < len; i++) {
    const router = routers[i]
    try {
      for (let i = 0, len = routes.length; i < len; i++) {
        router.add(...routes[i])
      }
      res = router.match(method, path)
    } catch (e) {
      if (e instanceof UnsupportedPathError) {
        continue
      }
      throw e
    }

    this.match = router.match.bind(router)
    this.#routers = [router]
    this.#routes = undefined
    break
  }
  // ...
}
SmartRouter locks onto the first compatible router after the first request, providing optimal performance for subsequent requests.

Benefits

  • Automatic optimization: Chooses the best router for your route patterns
  • Zero configuration: Works out of the box
  • Flexible: Supports all route patterns

RegExpRouter

A high-performance router that uses regular expressions for matching.
import { Hono } from 'hono'
import { RegExpRouter } from 'hono/router/reg-exp-router'

const app = new Hono({ router: new RegExpRouter() })

Characteristics

  • Performance: Fast matching using compiled regex patterns
  • Memory: Pre-builds matcher during initialization
  • Best for: Complex route patterns with many parameters

Implementation Details

From src/router/reg-exp-router/router.ts:122:
export class RegExpRouter<T> implements Router<T> {
  name: string = 'RegExpRouter'
  #middleware?: Record<string, Record<string, HandlerWithMetadata<T>[]>>
  #routes?: Record<string, Record<string, HandlerWithMetadata<T>[]>>

  constructor() {
    this.#middleware = { [METHOD_NAME_ALL]: Object.create(null) }
    this.#routes = { [METHOD_NAME_ALL]: Object.create(null) }
  }

  add(method: string, path: string, handler: T) {
    // Builds route data structures
    // ...
  }
}
The router uses a trie structure to build optimized regex patterns:
// From src/router/reg-exp-router/router.ts:34
function buildMatcherFromPreprocessedRoutes<T>(
  routes: [string, HandlerWithMetadata<T>[]][]
): Matcher<T> {
  const trie = new Trie()
  const handlerData: HandlerData<T>[] = []
  // Builds regex from trie
  const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp()
  // ...
}

TrieRouter

A router using a trie (prefix tree) data structure for efficient route matching.
import { Hono } from 'hono'
import { TrieRouter } from 'hono/router/trie-router'

const app = new Hono({ router: new TrieRouter() })

Characteristics

  • Performance: Excellent for static routes and simple patterns
  • Memory: Efficient memory usage
  • Best for: Applications with many static routes

Implementation

From src/router/trie-router/router.ts:5:
export class TrieRouter<T> implements Router<T> {
  name: string = 'TrieRouter'
  #node: Node<T>

  constructor() {
    this.#node = new Node()
  }

  add(method: string, path: string, handler: T) {
    const results = checkOptionalParameter(path)
    if (results) {
      for (let i = 0, len = results.length; i < len; i++) {
        this.#node.insert(method, results[i], handler)
      }
      return
    }

    this.#node.insert(method, path, handler)
  }

  match(method: string, path: string): Result<T> {
    return this.#node.search(method, path)
  }
}

LinearRouter

A simple router that linearly iterates through all routes.
import { Hono } from 'hono'
import { LinearRouter } from 'hono/router/linear-router'

const app = new Hono({ router: new LinearRouter() })

Characteristics

  • Simplicity: Straightforward implementation
  • Performance: Slower for many routes, but minimal overhead for few routes
  • Memory: Minimal memory footprint
  • Best for: Small applications or microservices with few routes

Implementation

From src/router/linear-router/router.ts:11:
export class LinearRouter<T> implements Router<T> {
  name: string = 'LinearRouter'
  #routes: [string, string, T][] = []

  add(method: string, path: string, handler: T) {
    for (
      let i = 0, paths = checkOptionalParameter(path) || [path], len = paths.length;
      i < len;
      i++
    ) {
      this.#routes.push([method, paths[i], handler])
    }
  }

  match(method: string, path: string): Result<T> {
    const handlers: [T, Params][] = []
    ROUTES_LOOP: for (let i = 0, len = this.#routes.length; i < len; i++) {
      const [routeMethod, routePath, handler] = this.#routes[i]
      if (routeMethod === method || routeMethod === METHOD_NAME_ALL) {
        // Pattern matching logic
        // ...
      }
    }
    return [handlers]
  }
}

PatternRouter

A router using regular expressions with named capture groups.
import { Hono } from 'hono'
import { PatternRouter } from 'hono/router/pattern-router'

const app = new Hono({ router: new PatternRouter() })

Characteristics

  • Flexibility: Supports complex regex patterns in route definitions
  • Performance: Good for moderate numbers of routes
  • Best for: Routes with custom regex patterns

Implementation

From src/router/pattern-router/router.ts:8:
export class PatternRouter<T> implements Router<T> {
  name: string = 'PatternRouter'
  #routes: Route<T>[] = []

  add(method: string, path: string, handler: T) {
    const endsWithWildcard = path.at(-1) === '*'
    if (endsWithWildcard) {
      path = path.slice(0, -2)
    }
    // Converts path to regex with named groups
    const parts = (path.match(/\/?(:w+(?:{(?:(?:{[\d,]+})|[^}])+})?)|\/[^\/\?]+/g) || []).map(
      (part) => {
        const match = part.match(/^\/:([^{]+)(?:{(.*)})?/)
        return match
          ? `/(?<${match[1]}>${match[2] || '[^/]+'})`
          : part === '/*'
            ? '/[^/]+'
            : part.replace(/[.\\+*[^\]$()]/g, '\\$&')
      }
    )
    // ...
  }
}

Performance Comparison

RegExpRouter

Best overall performance for most use cases. Recommended for production.

TrieRouter

Excellent for static routes. Lower memory usage than RegExpRouter.

SmartRouter

Automatically selects the best router. Good default choice.

LinearRouter

Simple and lightweight. Best for applications with very few routes.

PatternRouter

Flexible regex support. Good for complex patterns.

Choosing a Router

  • You want zero configuration
  • You’re unsure which router is best
  • Your route patterns vary in complexity
  • You want automatic optimization
  • You have many routes (100+)
  • You need maximum performance
  • You use complex route patterns
  • Memory usage is acceptable
  • Most routes are static
  • Memory efficiency is important
  • You have predictable route patterns
  • You don’t need complex regex patterns
  • You have very few routes (less than 10)
  • Simplicity is a priority
  • Memory footprint must be minimal
  • You’re building a microservice
  • You need custom regex patterns
  • Route complexity varies widely
  • You want named capture groups

Specifying a Router

Set the router when creating your Hono instance:
import { Hono } from 'hono'
import { RegExpRouter } from 'hono/router/reg-exp-router'

const app = new Hono({ router: new RegExpRouter() })
From src/hono-base.ts:65, the router option is passed during construction:
export type HonoOptions<E extends Env> = {
  strict?: boolean
  router?: Router<[H, RouterRoute]>
  getPath?: GetPath<E>
}

Unsupported Paths

Some routers may throw UnsupportedPathError for certain route patterns:
// From src/router.ts:103
export class UnsupportedPathError extends Error {}
SmartRouter handles this by trying alternative routers:
try {
  router.add(...routes[i])
  res = router.match(method, path)
} catch (e) {
  if (e instanceof UnsupportedPathError) {
    continue // Try next router
  }
  throw e
}

Build docs developers (and LLMs) love