Nuxt
Source:
src/Cloudflare/Website/Nuxt.ts
A Cloudflare Worker deployed from a Nuxt project.
Nuxt builds the app programmatically through the project’s own
@nuxt/kit with nitro’s cloudflare_module preset — the project’s
nuxt.config.ts loads natively, no nitro.preset edits, no Wrangler
configuration, and no build command required. The nitro server bundle
deploys as the Worker script; client assets and prerendered pages
(.output/public) deploy as Worker static assets.
Requires the @distilled.cloud/nuxt package to be installed in your
project (alongside nuxt itself). Input files are content-hashed
(respecting .gitignore by default) so unchanged projects skip the
build and deploy entirely.
The server build uses nitro’s hybrid workerd Node compatibility
(cloudflare.nodeCompat), which relies on workerd’s native node:*
modules — the nodejs_compat compatibility flag is always included in
the Worker’s compatibility flags to match.
On local dev: alchemy dev runs Nuxt’s own dev server (nitro dev, SSR
in a Node worker thread, full HMR) with the Worker’s bindings served on
event.context.cloudflare through cloudflare-runtime’s platform proxy —
wrangler-free. Literal env values overlay the proxied bindings;
resource bindings (KV, R2, D1, …) round-trip to the proxy’s local
workerd instance, so dev state is live and shared. Durable Objects
declared via a custom main entry only exist in the production build
and are not servable in dev yet.
Deploying a Nuxt App
Section titled “Deploying a Nuxt App”A single call builds and deploys the app — server-rendered pages, API routes, prerendered pages, and client assets included.
Basic Nuxt site
const site = yield* Cloudflare.Website.Nuxt("Website");Nuxt project in a subdirectory
const site = yield* Cloudflare.Website.Nuxt("Website", { rootDir: "apps/web",});Bindings
Section titled “Bindings”Values passed via env are exposed to server routes and SSR through
nitro’s cloudflare_module runtime contract:
event.context.cloudflare.env (plus event.context.cf and
event.context.cloudflare.context.waitUntil).
Reading env from an API route
const site = yield* Cloudflare.Website.Nuxt("Website", { env: { API_KEY: Config.redacted("API_KEY"), },});
// server/api/hello.ts// export default defineEventHandler((event) => ({// hasKey: event.context.cloudflare?.env?.API_KEY !== undefined,// }));Binding an R2 bucket
const bucket = yield* Cloudflare.R2.Bucket("Uploads");
const site = yield* Cloudflare.Website.Nuxt("Website", { env: { UPLOADS: bucket, },});Prerendering
Section titled “Prerendering”Routes marked for prerendering in routeRules (or via
nitro.prerender) render at build time into .output/public and are
served as static assets — no Worker invocation.
const site = yield* Cloudflare.Website.Nuxt("Website", { nuxt: { routeRules: { "/about": { prerender: true }, }, },});Custom Worker Exports (Durable Objects)
Section titled “Custom Worker Exports (Durable Objects)”Nitro’s entry module is the Worker’s exports seam. Point main at your
own module that re-exports nitro’s runtime handler (imported from
nitropack/presets/cloudflare/runtime/cloudflare-module) and adds
extra exports — Durable Object classes must live on the deployed
Worker for their namespace bindings to resolve. Every framework route
keeps working through the re-exported handler.
// import nitroHandler from "nitropack/presets/cloudflare/runtime/cloudflare-module";// export class Counter extends DurableObject { ... }// export default nitroHandler;
const site = yield* Cloudflare.Website.Nuxt("Website", { main: "worker-entry.ts", env: { COUNTER: Cloudflare.DurableObject("Counter", { className: "Counter", }), },});alchemy dev runs Nuxt’s own dev server (nitro dev, full HMR) with
event.context.cloudflare served wrangler-free through
cloudflare-runtime’s platform proxy: resource bindings resolve against
a local workerd instance, and literal env values overlay them.
// server/api/greeting.ts — identical code in dev and deployed// export default defineEventHandler((event) => ({// greeting: event.context.cloudflare?.env?.GREETING,// }));const site = yield* Cloudflare.Website.Nuxt("Website", { env: { GREETING: "hello" },});Custom Rebuild Scope
Section titled “Custom Rebuild Scope”By default, every non-gitignored file is hashed to decide whether a
rebuild is needed. Use memo to narrow the scope when the project
lives in a large repository.
const site = yield* Cloudflare.Website.Nuxt("Website", { memo: { include: ["app/**", "server/**", "public/**", "nuxt.config.ts", "package.json"], },});Limitations
Section titled “Limitations”Nitro’s isr route rule (incremental static regeneration) is
implemented only by the Vercel and Netlify presets — on Cloudflare it
is silently ignored at build time, and the route renders on demand in
the Worker like any other SSR route. Use prerender for build-time
static routes, or cache route rules for runtime caching.
Class Form
Section titled “Class Form”Calling Nuxt with no arguments returns a constructor you can
extend to declare the Worker as a named class. The class is both an
Effect you can yield* to deploy and a type you can reference
elsewhere — useful when other resources need to bind to this Worker.
class Website extends Cloudflare.Website.Nuxt<Website>()( "Website",) {}
const site = yield* Website;