The SmartRouter is a meta-router that automatically chooses the optimal routing implementation based on your actual route patterns. It attempts to use the fastest router (RegExpRouter) first, and falls back to more compatible routers (like TrieRouter) if unsupported path patterns are detected.
The SmartRouter requires explicit router candidates to be provided:
import { Hono } from 'hono'import { SmartRouter } from 'hono/router/smart-router'import { RegExpRouter } from 'hono/router/reg-exp-router'import { TrieRouter } from 'hono/router/trie-router'const app = new Hono({ router: new SmartRouter({ routers: [new RegExpRouter(), new TrieRouter()] })})
Routers are tried in the order provided. Always put faster routers first:
// ✅ Recommended: Fastest firstconst router = new SmartRouter({ routers: [ new RegExpRouter(), // Try fastest first new TrieRouter(), // Fallback to most compatible ]})// ❌ Not optimal: Slower router might be selected unnecessarilyconst router = new SmartRouter({ routers: [ new TrieRouter(), // Will always succeed new RegExpRouter(), // Never reached ]})
import { SmartRouter } from 'hono/router/smart-router'import { RegExpRouter } from 'hono/router/reg-exp-router'import { TrieRouter } from 'hono/router/trie-router'import { PatternRouter } from 'hono/router/pattern-router'// Try multiple routers in priority orderconst router = new SmartRouter({ routers: [ new RegExpRouter(), // Fastest, but limited patterns new PatternRouter(), // Moderate speed, more patterns new TrieRouter(), // Slowest, all patterns supported ]})const app = new Hono({ router })
// Routes:app.get('/', handler)app.get('/users/:id', handler)app.get('/posts/:slug', handler)// Result: RegExpRouter selected// Reason: All routes are supported, best performance
// Routes:app.get('/api/users', handler)app.get('/files/:name/*', handler) // Unsupported by RegExpRouter// Result: TrieRouter selected// Reason: RegExpRouter fails on wildcard+parameter combination
// Routes:app.get('/static/*', handler)app.get('/api/:version/users', handler)// Result: RegExpRouter selected (if listed first)// Reason: All patterns are supported, first router wins
SmartRouter updates its name to reflect the active router:
const router = new SmartRouter({ routers: [new RegExpRouter(), new TrieRouter()]})console.log(router.name) // "SmartRouter"// After first requestawait app.fetch(request)console.log(router.name) // "SmartRouter + RegExpRouter"
After selection, SmartRouter replaces its own match method:
// Before first requestsmartRouter.match(method, path) // Runs selection logic// After first requestsmartRouter.match(method, path) // Direct delegation to active router// No performance penalty!
SmartRouter stores routes without processing them:
const router = new SmartRouter({ routers: [new RegExpRouter()] })const app = new Hono({ router })// These calls are very fast - just collecting dataapp.get('/route1', handler1) // Storedapp.get('/route2', handler2) // Storedapp.get('/route3', handler3) // Stored// No trie building, no RegExp compilation yet!// First request triggers selection and buildingawait app.fetch(request) // Now routes are processed
const router = new SmartRouter({ routers: [new RegExpRouter(), new TrieRouter()]})// Before any requeststry { const active = router.activeRouter} catch (error) { // Error: "No active router has been determined yet."}
// Strategy 1: Pre-warm the router during startupconst router = new SmartRouter({ routers: [new RegExpRouter(), new TrieRouter()]})const app = new Hono({ router })// Define all routesapp.get('/', handler)app.get('/users/:id', handler)// Trigger router selection before accepting trafficawait app.fetch(new Request('http://localhost/'))console.log('Router selected:', router.activeRouter.name)// Now ready for production traffic!// Strategy 2: Use SmartRouter in development, direct router in productionconst router = process.env.NODE_ENV === 'production' ? new RegExpRouter() // Known to work with our routes : new SmartRouter({ routers: [new RegExpRouter(), new TrieRouter()] })