hono-preact

Build & Deploy

How you build and deploy a hono-preact app depends on the adapter it targets. This page covers development, production builds, and deployment for each shipped adapter.

Adapters

The framework targets whichever adapter you choose. Two adapters ship today: cloudflareAdapter() from hono-preact/adapter-cloudflare and nodeAdapter() from hono-preact/adapter-node. You select one in vite.config.ts by passing it to the framework plugin: honoPreact({ adapter: ... }). An adapter is required.

Dev, build, and deploy all behave per-adapter. The rest of this page covers both flows.

Cloudflare Workers

This section assumes a cloudflareAdapter() in vite.config.ts:

import { cloudflareAdapter } from 'hono-preact/adapter-cloudflare';
// ...
plugins: [honoPreact({ adapter: cloudflareAdapter() })],

Development

npm run dev

Starts the Vite dev server. The Cloudflare adapter runs your worker inside workerd (the real Cloudflare Workers runtime) via @cloudflare/vite-plugin, so development mirrors production: Cloudflare bindings, the SSR runtime, and WebSocket upgrades all behave the same as a deployed Worker. Hot module replacement works for both client components and server code.

Production build

npm run build

A single vite build. Vite's Environment API builds the browser bundle and the Worker bundle together; there is no separate client pass. The serverOnlyPlugin still replaces every *.server.* import with a no-op stub so server code never enters the browser bundle.

Output is split into two directories so worker source can never be served as a static asset:

dist/
  client/                      # static assets, served from Cloudflare's CDN
    static/client.js            #   client entry
    static/<name>-<hash>.js     #   lazy route chunks
    static/<name>-<hash>.css
  hono_preact/                 # the Worker bundle
    index.js                    #   bundled Worker
    wrangler.json               #   generated deploy config

The Worker directory name is derived from the name field in wrangler.jsonc (hyphens become underscores), so hono-preact produces dist/hono_preact/.

Local preview

npm run preview

Runs vite preview, which serves the production build through workerd again.

Configuring wrangler.jsonc

wrangler.jsonc is the source of truth @cloudflare/vite-plugin reads:

{
  "name": "your-app-name", // also names the dist/<name>/ worker directory
  "main": "node_modules/.vite/hono-preact/server-entry.tsx",
  "compatibility_date": "2026-02-22",
  "compatibility_flags": ["nodejs_compat"],
  "assets": {
    "directory": "./dist/client",
  },
}

main points at the server entry the framework generates into the Vite cache directory; the framework writes that file before the build starts. assets points at the client build output. No run_worker_first list is needed: the client assets and the Worker bundle land in separate directories, so the Worker source is never exposed through the asset store. The build also writes a .assetsignore into dist/client/ that excludes the generated config from the asset upload.

Deploy

npm run deploy

This runs wrangler deploy against the generated config in the Worker output directory (dist/hono_preact/wrangler.json), which references the built Worker and the dist/client/ assets. Run npm run build first so the output exists.

Node.js

Setup

Register the Node adapter in vite.config.ts:

import { honoPreact } from 'hono-preact/vite';
import { nodeAdapter } from 'hono-preact/adapter-node';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [honoPreact({ adapter: nodeAdapter() })],
});

@hono/node-server is a peer dependency, so install it alongside the framework: npm install @hono/node-server. @hono/node-ws is an optional peer if you need WebSocket upgrades.

Development

npm run dev

Starts the Vite dev server. The Node adapter handles SSR through Vite's SSR module runner, so HMR works for both client and server code.

Production build and start

vite build emits a server entry at dist/server/server-entry.js and the client bundle under dist/client/. Start production with:

node dist/server/server-entry.js

The server listens on PORT (default 3000).

Deploying

Deploy the same way you would any Node server: a container image (Docker), a system service (systemd), or a PaaS that runs Node processes. Static assets must be served from dist/client/; the Node adapter serves them in-process when you run the bundled server, so a single node process handles both assets and SSR.

See also

  • Vite Config: full honoPreact() options reference and peer-dependency setup.
  • Composing Hono Middleware: adding custom Hono routes and middleware alongside the framework (via src/api.ts).