hono
Skill3 starsChanged 8 months agoApache-2.0View on GitHub
Use when building Hono web applications deployed to Vercel, or when the user asks about Hono APIs, routing, middleware, JSX, validation, testing, or streaming. TRIGGER when code imports from 'hono' or 'hono/*', or user mentions Hono. Use `npx hono request` to test endpoints.
- Reads credentials
22 other projects use a version of this, 12 of them edited.
Adopted by
Most recently taken up by
- ptonlix/OneSelfedited, first seen Nov 8, 2026
- louisbrulenaudet/monorepo-templateedited, first seen Oct 29, 2026
- philtzjp/skillstheir own version, first seen Oct 28, 2026
- Marvellbrazil/my-agentic-skillstheir own version, first seen Oct 17, 2026
- rabius-sunny/agent-skillstheir own version, first seen Oct 13, 2026
- G1Joshi/Agent-Skillstheir own version, first seen Oct 13, 2026
Used with
Projects with it also use turborepo, better-auth-best-practices and 9 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
VercelSkillturborepo
Turborepo monorepo build system guidance. Triggers on: turbo.json, task pipelines, dependsOn, caching, remote cache, the "turbo" CLI, --filter, --affected, CI optimization, environment variables, internal packages, monorepo structure/best practices, and boundaries.
Also in 5 projects that use this
anasahmed07Skillbetter-auth-best-practices
Skill for integrating Better Auth - the comprehensive TypeScript authentication framework.
Also in 5 projects that use this
SentrySkillvercel-composition-patterns
Also in 7 projects that use this
VercelSkillvercel-react-best-practices
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
Also in 8 projects that use this
VercelSkillweb-design-guidelines
Review UI code for Web Interface Guidelines compliance. Use when asked to "review my UI", "check accessibility", "audit design", "review UX", or "check my site against best practices".
Also in 5 projects that use this
DatabricksSkillshadcn
Manages shadcn components and projects — adding, searching, fixing, debugging, styling, and composing UI. Provides project context, component docs, and usage examples. Applies when working with shadcn/ui, component registries, presets, --preset codes, or any project with a components.json file. Also triggers for "shadcn init", "create an app with --preset", or "switch to --preset".
Also in 4 projects that use this
Other versions22 projects use a version of this.
Word for word · 0
None found.
Edited · 12
- pedronauck/skills★ 65554% same
- freestyle-voice/freestyle★ 49091% same
- ptonlix/OneSelf★ 5354% same
- louisbrulenaudet/monorepo-template★ 1954% same
- honojs/skills★ 1754% same
- getsentry/outpost★ 591% same
- arvindrk/twitter-agent★ 491% same
- buduan/soul-graph★ 291% same
- JustineDevs/CRUX★ 191% same
- rockcookies/skills★ 154% same
2 more not listed
Wrote their own version · 10
- epicenter-md/epicenter★ 4.8ksame name
- EpicenterHQ/epicenter★ 4.8ksame name
- yusukebe/skills★ 36same name
- G1Joshi/Agent-Skills★ 15same name
- LeonardoTrapani/better-skills★ 11same name
- Marvellbrazil/my-agentic-skills★ 4same name
- yumazak/joy★ 4same name
- FelipeAngeli/skills★ 2same name
- philtzjp/skills★ 2same name
- rabius-sunny/agent-skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository7 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- chat-sdkSkill
- honoSkill
- slack-agentSkill
- chat-sdkSkill
- slack-agentSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
14 rules. 0 are common, 4 are distinctive, and 10 are very specific to this project.
0
Common
Most of the 21 edited versions we read keep these.
4
Distinctive
This author's own choices. Worth a look.
10
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 14
Test endpoints without starting an HTTP server.
- Distinctive: line 24
Note: Do not pass credentials directly in CLI arguments.
- Very specific: line 51
No Node.js adapter (@hono/node-server) is needed for Vercel — just use the default export.
- Distinctive: line 55
Use Vercel CLI to run locally with the same behavior as production:
- Very specific: line 63
Place static files in the public/ directory.
- Very specific: line 421
Use jsxRenderer middleware for layouts.
- Distinctive: line 494
Test endpoints without starting an HTTP server:
- Very specific: line 578
Use createFactory to define Env once and share it across app, middleware, and handlers:
- Distinctive: line 602
Write handlers inline in route definitions for proper type inference of path params.
- Very specific: line 603
Use app.route() to organize large apps by feature, not Rails-style controllers.
- Very specific: line 604
Use createFactory() to share Env type across app, middleware, and handlers.
- Very specific: line 605
Use c.set()/c.get() to pass data between middleware and handlers.
- Very specific: line 608
Use app.request() for testing — no server startup needed.
- Very specific: line 625
Keep this in a separate file (e.g., src/lib/local.ts) so it doesn't affect the Vercel deployment.
“Common” is measured: kept by at least 6 in 10 of the 21 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Hono Skill
Build Hono web applications and deploy them to Vercel with zero configuration. This skill provides inline API knowledge for AI. Use npx hono request to test endpoints. If the hono-docs MCP server is configured, prefer its tools for the latest documentation over the inline reference.
Hono CLI Usage
Request Testing
Test endpoints without starting an HTTP server. Uses app.request() internally.
# GET request
npx hono request [file] -P /path
# POST request with JSON body
npx hono request [file] -X POST -P /api/users -d '{"name": "test"}'Note: Do not pass credentials directly in CLI arguments. Use environment variables for sensitive values.
Deploying Hono on Vercel
Hono apps deploy to Vercel with zero configuration. Your server routes automatically become Vercel Functions using Fluid compute by default.
File Placement
Export your Hono app as the default export from any of these locations:
app.{js,cjs,mjs,ts,cts,mts}index.{js,cjs,mjs,ts,cts,mts}server.{js,cjs,mjs,ts,cts,mts}src/app.{js,cjs,mjs,ts,cts,mts}src/index.{js,cjs,mjs,ts,cts,mts}src/server.{js,cjs,mjs,ts,cts,mts}
import { Hono } from 'hono'
const app = new Hono()
// ... routes ...
export default appNo Node.js adapter (@hono/node-server) is needed for Vercel — just use the default export. Use @hono/node-server only for local development if not using vc dev.
Local Development
Use Vercel CLI to run locally with the same behavior as production:
vc devStatic Assets
Place static files in the public/ directory. They are served via the Vercel CDN with default headers.
Important: Hono's serveStatic() is ignored on Vercel — use the public/ directory instead.
Streaming
Vercel Functions support streaming, which works with Hono's stream(), streamText(), and streamSSE() helpers out of the box.
Middleware: Hono vs Vercel
- Hono Middleware — Runs inside your app before route handlers (CORS, auth, logging, etc.)
- Vercel Routing Middleware — Runs at the edge before your app is invoked (rewrites, redirects, headers). See Vercel Routing Middleware docs.
Both can be used together.
Hono API Reference
App Constructor
import { Hono } from 'hono'
const app = new Hono()
// With TypeScript generics for request-scoped variables
type Env = {
Variables: { user: User }
}
const app = new Hono<Env>()Routing Methods
app.get('/path', handler)
app.post('/path', handler)
app.put('/path', handler)
app.delete('/path', handler)
app.patch('/path', handler)
app.options('/path', handler)
app.all('/path', handler) // all HTTP methods
app.on('PURGE', '/path', handler) // custom method
app.on(['PUT', 'DELETE'], '/path', handler) // multiple methodsRouting Patterns
// Path parameters
app.get('/user/:name', (c) => {
const name = c.req.param('name')
return c.json({ name })
})
// Multiple params
app.get('/posts/:id/comments/:commentId', (c) => {
const { id, commentId } = c.req.param()
})
// Optional parameters
app.get('/api/animal/:type?', (c) => c.text('Animal!'))
// Wildcards
app.get('/wild/*/card', (c) => c.text('Wildcard'))
// Regexp constraints
app.get('/post/:date{[0-9]+}/:title{[a-z]+}', (c) => {
const { date, title } = c.req.param()
})
// Chained routes
app
.get('/endpoint', (c) => c.text('GET'))
.post((c) => c.text('POST'))
.delete((c) => c.text('DELETE'))Route Grouping
// Using route()
const api = new Hono()
api.get('/users', (c) => c.json([]))
const app = new Hono()
app.route('/api', api) // mounts at /api/users
// Using basePath()
const app = new Hono().basePath('/api')
app.get('/users', (c) => c.json([])) // GET /api/usersError Handling
app.notFound((c) => c.json({ message: 'Not Found' }, 404))
app.onError((err, c) => {
console.error(err)
return c.json({ message: 'Internal Server Error' }, 500)
})Context (c)
Response Methods
c.text('Hello') // text/plain
c.json({ message: 'Hello' }) // application/json
c.html('<h1>Hello</h1>') // text/html
c.redirect('/new-path') // 302 redirect
c.redirect('/new-path', 301) // 301 redirect
c.body('raw body', 200, headers) // raw response
c.notFound() // 404 responseHeaders & Status
c.status(201)
c.header('X-Custom', 'value')
c.header('Cache-Control', 'no-store')Variables (request-scoped data)
// In middleware
c.set('user', { id: 1, name: 'Alice' })
// In handler
const user = c.get('user')
// or
const user = c.var.userEnvironment Variables
On Vercel, use process.env to access environment variables:
const apiKey = process.env.API_KEYRenderer
app.use(async (c, next) => {
c.setRenderer((content) =>
c.html(
<html><body>{content}</body></html>
)
)
await next()
})
app.get('/', (c) => c.render(<h1>Hello</h1>))HonoRequest (c.req)
c.req.param('id') // path parameter
c.req.param() // all path params as object
c.req.query('page') // query string parameter
c.req.query() // all query params as object
c.req.queries('tags') // multiple values: ?tags=A&tags=B → ['A', 'B']
c.req.header('Authorization') // request header
c.req.header() // all headers (keys are lowercase)
// Body parsing
await c.req.json() // parse JSON body
await c.req.text() // parse text body
await c.req.formData() // parse as FormData
await c.req.parseBody() // parse multipart/form-data or urlencoded
await c.req.arrayBuffer() // parse as ArrayBuffer
await c.req.blob() // parse as Blob
// Validated data (used with validator middleware)
c.req.valid('json')
c.req.valid('query')
c.req.valid('form')
c.req.valid('param')
// Properties
c.req.url // full URL string
c.req.path // pathname
c.req.method // HTTP method
c.req.raw // underlying Request objectMiddleware
Using Built-in Middleware
import { cors } from 'hono/cors'
import { logger } from 'hono/logger'
import { basicAuth } from 'hono/basic-auth'
import { prettyJSON } from 'hono/pretty-json'
import { secureHeaders } from 'hono/secure-headers'
import { etag } from 'hono/etag'
import { compress } from 'hono/compress'
import { poweredBy } from 'hono/powered-by'
import { timing } from 'hono/timing'
import { cache } from 'hono/cache'
import { bearerAuth } from 'hono/bearer-auth'
import { jwt } from 'hono/jwt'
import { csrf } from 'hono/csrf'
import { ipRestriction } from 'hono/ip-restriction'
import { bodyLimit } from 'hono/body-limit'
import { requestId } from 'hono/request-id'
import { methodOverride } from 'hono/method-override'
import { trailingSlash, trimTrailingSlash } from 'hono/trailing-slash'
// Registration
app.use(logger()) // all routes
app.use('/api/*', cors()) // specific path
app.post('/api/*', basicAuth({ username: 'admin', password: 'secret' }))Custom Middleware
// Inline
app.use(async (c, next) => {
const start = Date.now()
await next()
const elapsed = Date.now() - start
c.res.headers.set('X-Response-Time', `${elapsed}ms`)
})
// Reusable with createMiddleware
import { createMiddleware } from 'hono/factory'
const auth = createMiddleware(async (c, next) => {
const token = c.req.header('Authorization')
if (!token) return c.json({ error: 'Unauthorized' }, 401)
await next()
})
app.use('/api/*', auth)Middleware Execution Order
Middleware executes in registration order. await next() calls the next middleware/handler, and code after next() runs on the way back:
Request → mw1 before → mw2 before → handler → mw2 after → mw1 after → Responseapp.use(async (c, next) => {
// before handler
await next()
// after handler
})Validation
Validation targets: json, form, query, header, param, cookie.
Zod Validator
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
const schema = z.object({
title: z.string().min(1),
body: z.string()
})
app.post('/posts', zValidator('json', schema), (c) => {
const data = c.req.valid('json') // fully typed
return c.json(data, 201)
})Valibot / Standard Schema Validator
import { sValidator } from '@hono/standard-validator'
import * as v from 'valibot'
const schema = v.object({ name: v.string(), age: v.number() })
app.post('/users', sValidator('json', schema), (c) => {
const data = c.req.valid('json')
return c.json(data, 201)
})JSX
Setup
In tsconfig.json:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx"
}
}Or use pragma: /** @jsxImportSource hono/jsx */
Important: Files using JSX must have a .tsx extension. Rename .ts to .tsx or the compiler will fail.
Components
import type { PropsWithChildren } from 'hono/jsx'
const Layout = (props: PropsWithChildren) => (
<html>
<head>
<title>My App</title>
</head>
<body>{props.children}</body>
</html>
)
const UserCard = ({ name }: { name: string }) => (
<div class="card">
<h2>{name}</h2>
</div>
)
app.get('/', (c) => {
return c.html(
<Layout>
<UserCard name="Alice" />
</Layout>
)
})jsxRenderer Middleware
Use jsxRenderer middleware for layouts. See npx hono docs /docs/middleware/builtin/jsx-renderer for details.
Async Components
const UserList = async () => {
const users = await fetchUsers()
return (
<ul>
{users.map((u) => (
<li>{u.name}</li>
))}
</ul>
)
}Fragments
const Items = () => (
<>
<li>Item 1</li>
<li>Item 2</li>
</>
)Streaming
import { stream, streamText, streamSSE } from 'hono/streaming'
// Basic stream
app.get('/stream', (c) => {
return stream(c, async (stream) => {
stream.onAbort(() => console.log('Aborted'))
await stream.write(new Uint8Array([0x48, 0x65]))
await stream.pipe(readableStream)
})
})
// Text stream
app.get('/stream-text', (c) => {
return streamText(c, async (stream) => {
await stream.writeln('Hello')
await stream.sleep(1000)
await stream.write('World')
})
})
// Server-Sent Events
app.get('/sse', (c) => {
return streamSSE(c, async (stream) => {
let id = 0
while (true) {
await stream.writeSSE({
data: JSON.stringify({ time: new Date().toISOString() }),
event: 'time-update',
id: String(id++)
})
await stream.sleep(1000)
}
})
})Testing with app.request()
Test endpoints without starting an HTTP server:
// GET
const res = await app.request('/posts')
expect(res.status).toBe(200)
expect(await res.json()).toEqual({ posts: [] })
// POST with JSON
const res = await app.request('/posts', {
method: 'POST',
body: JSON.stringify({ title: 'Hello' }),
headers: { 'Content-Type': 'application/json' }
})
// POST with FormData
const formData = new FormData()
formData.append('name', 'Alice')
const res = await app.request('/users', { method: 'POST', body: formData })
// Using Request object
const req = new Request('http://localhost/api', { method: 'DELETE' })
const res = await app.request(req)Hono Client (RPC)
Type-safe API client using shared types between server and client.
IMPORTANT: Routes MUST be chained for type inference to work. Without chaining, the client cannot infer route types.
// Server: routes MUST be chained to preserve types
const route = app
.post('/posts', zValidator('json', schema), (c) => {
return c.json({ ok: true }, 201)
})
.get('/posts', (c) => {
return c.json({ posts: [] })
})
export type AppType = typeof route
// Client: use hc() with the exported type
import { hc } from 'hono/client'
import type { AppType } from './server'
const client = hc<AppType>('http://localhost:8787/')
const res = await client.posts.$post({ json: { title: 'Hello' } })
const data = await res.json() // fully typedType utilities:
import type { InferRequestType, InferResponseType } from 'hono/client'
type ReqType = InferRequestType<typeof client.posts.$post>
type ResType = InferResponseType<typeof client.posts.$post, 200>Helpers
Helpers are utility functions imported from hono/<helper-name>:
import { getConnInfo } from 'hono/conninfo'
import { getCookie, setCookie, deleteCookie } from 'hono/cookie'
import { css, Style } from 'hono/css'
import { createFactory } from 'hono/factory'
import { html, raw } from 'hono/html'
import { stream, streamText, streamSSE } from 'hono/streaming'
import { testClient } from 'hono/testing'Available helpers: Accepts, Adapter, ConnInfo, Cookie, css, Dev, Factory, html, JWT, Proxy, Route, SSG, Streaming, Testing.
For details, use npx hono docs /docs/helpers/<helper-name>.
Factory
Use createFactory to define Env once and share it across app, middleware, and handlers:
import { createFactory } from 'hono/factory'
const factory = createFactory<Env>()
// Create app (Env type is inherited)
const app = factory.createApp()
// Create middleware (Env type is inherited, no need to pass generics)
const mw = factory.createMiddleware(async (c, next) => {
await next()
})
// Create handlers separately (preserves type inference)
const handlers = factory.createHandlers(logger(), (c) => c.json({ message: 'Hello' }))
app.get('/api', ...handlers)Best Practices
- Write handlers inline in route definitions for proper type inference of path params.
- Use
app.route()to organize large apps by feature, not Rails-style controllers. - Use
createFactory()to share Env type across app, middleware, and handlers. - Use
c.set()/c.get()to pass data between middleware and handlers. - Chain validators for multiple request parts (param + query + json).
- Export app type for RPC:
export type AppType = typeof routes - Use
app.request()for testing — no server startup needed.
Vercel Deployment
On Vercel, just use the default export — no adapter needed:
export default appFor local development without vc dev, you can use the Node.js adapter:
import { serve } from '@hono/node-server'
serve(app)Keep this in a separate file (e.g., src/lib/local.ts) so it doesn't affect the Vercel deployment.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

