Cloudflare Pages is a JAMstack platform that supports serverless functions. You can deploy full-stack Hono applications with Pages Functions.
Quick Start
Create a new Hono project for Cloudflare Pages:
npm create hono@latest my-app
Select “cloudflare-pages” as your template when prompted.
Installation
Add Hono to your existing Cloudflare Pages project:
Basic Usage
Cloudflare Pages uses a functions directory for serverless functions. You can use Hono with the advanced mode.
Advanced Mode Setup
Create functions/[[route]].ts to handle all routes:
import { Hono } from 'hono'
import { handle } from 'hono/cloudflare-pages'
const app = new Hono()
app.get('/', (c) => c.text('Hello Cloudflare Pages!'))
app.get('/api/hello', (c) => {
return c.json({ message: 'Hello from Pages Functions!' })
})
export const onRequest = handle(app)
The handle adapter converts your Hono app into a Pages Functions handler.
Adapter Features
Handle Function
The handle function wraps your Hono app for Cloudflare Pages:
import { Hono } from 'hono'
import { handle } from 'hono/cloudflare-pages'
const app = new Hono()
app.get('/api/*', (c) => {
return c.json({ message: 'API route' })
})
export const onRequest = handle(app)
Middleware Handler
Use handleMiddleware to create middleware for Pages Functions:
import { handleMiddleware } from 'hono/cloudflare-pages'
import { logger } from 'hono/logger'
export const onRequest = handleMiddleware(logger())
Accessing EventContext
Access the Cloudflare Pages eventContext:
import { Hono } from 'hono'
import { handle } from 'hono/cloudflare-pages'
import type { EventContext } from 'hono/cloudflare-pages'
type Env = {
Bindings: {
eventContext: EventContext
MY_KV: KVNamespace
}
}
const app = new Hono<Env>()
app.get('/api/context', (c) => {
const { eventContext } = c.env
// Use waitUntil for background tasks
eventContext.waitUntil(
fetch('https://api.example.com/log')
)
return c.json({ message: 'Task scheduled' })
})
export const onRequest = handle(app)
Static Files
Serve static files from the public directory:
import { Hono } from 'hono'
import { handle, serveStatic } from 'hono/cloudflare-pages'
const app = new Hono()
// Serve static files first
app.use('/static/*', serveStatic())
app.get('/api/hello', (c) => {
return c.json({ message: 'Hello!' })
})
export const onRequest = handle(app)
Connection Info
Get connection information:
import { Hono } from 'hono'
import { handle, getConnInfo } from 'hono/cloudflare-pages'
const app = new Hono()
app.get('/info', (c) => {
const info = getConnInfo(c)
return c.json({
remote: info.remote,
})
})
export const onRequest = handle(app)
Accessing Bindings
Access KV, D1, R2, and other bindings:
import { Hono } from 'hono'
import { handle } from 'hono/cloudflare-pages'
type Bindings = {
MY_KV: KVNamespace
DB: D1Database
}
const app = new Hono<{ Bindings: Bindings }>()
app.get('/kv/:key', async (c) => {
const key = c.req.param('key')
const value = await c.env.MY_KV.get(key)
return c.text(value || 'Not found')
})
app.get('/db/users', async (c) => {
const { results } = await c.env.DB.prepare(
'SELECT * FROM users'
).all()
return c.json(results)
})
export const onRequest = handle(app)
Configuration
Configure bindings in wrangler.toml:
name = "my-hono-pages"
pages_build_output_dir = "./public"
[[kv_namespaces]]
binding = "MY_KV"
id = "your-kv-id"
[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "your-database-id"
Deployment
Build your project
Build your frontend and backend: Deploy with Wrangler
Deploy using Wrangler:wrangler pages deploy public
Deploy via Git
Alternatively, connect your Git repository in the Cloudflare dashboard:
- Go to Cloudflare Pages
- Click “Create a project”
- Connect your repository
- Configure build settings
- Deploy
Project Structure
Typical Cloudflare Pages + Hono project structure:
my-app/
├── functions/
│ ├── [[route]].ts # Main API handler
│ └── _middleware.ts # Global middleware
├── public/
│ ├── index.html
│ └── static/
├── wrangler.toml
└── package.json
Development
Run locally with Wrangler:
wrangler pages dev public
This starts a local server at http://localhost:8788 with hot reload.
Combining with Frontend Frameworks
Cloudflare Pages works great with frontend frameworks:
With React/Vite
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "wrangler pages dev dist"
}
}
With Next.js
Use the @cloudflare/next-on-pages adapter for Next.js on Cloudflare Pages.
Best Practices
Separate API and static routes
Use path prefixes like /api/* for backend routes to avoid conflicts with static files.
Advanced Mode (functions/[[route]].ts) gives you full control over routing.
Use KV, D1, and R2 for data storage instead of external databases.
Keep your functions directory small for faster cold starts.
Resources
Cloudflare Pages Docs
Official Cloudflare Pages documentation
Pages Functions
Learn about Pages Functions