Skip to main content
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:
npm install hono

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:
functions/[[route]].ts
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:
functions/[[route]].ts
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:
functions/_middleware.ts
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:
functions/[[route]].ts
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:
functions/[[route]].ts
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:
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

1

Build your project

Build your frontend and backend:
npm run build
2

Deploy with Wrangler

Deploy using Wrangler:
wrangler pages deploy public
3

Deploy via Git

Alternatively, connect your Git repository in the Cloudflare dashboard:
  1. Go to Cloudflare Pages
  2. Click “Create a project”
  3. Connect your repository
  4. Configure build settings
  5. 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

package.json
{
  "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

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

Build docs developers (and LLMs) love