1# @alchemy.run/frontend-frameworks/nextjs
2
3Next.js framework integration for Cloudflare Workers — an OpenNext-based
4build pipeline and two dev-server modes, fully **wrangler-free**.
5
6- `build` runs the `@opennextjs/cloudflare` pipeline in a disposable child
7 process (no wrangler code is ever imported), performs the final bundle pass
8 wrangler would normally do at deploy time, and returns the framework-core
9 `BuildOutput` (server modules entry-first + the static-assets directory).
10- `dev` starts a local server in one of two modes (`dev.mode`).
11
12## Dev modes
13
14### `"preview"` (default) — production parity, no HMR
15
16Builds the OpenNext worker and serves it under
17`@alchemy.run/cloudflare-runtime/core` (workerd) with the OpenNext worker
18shape: `ASSETS` (run-worker-first), a `WORKER_SELF_REFERENCE` self service
19binding, and the same-script SQLite-backed revalidation-queue Durable Object.
20What you run is what you deploy. Any change requires a rebuild.
21
22### `"hmr"` — real `next dev`, Cloudflare bindings proxied
23
24Runs the programmatic `next({ dev: true })` custom-server API (Turbopack HMR)
25on an http server this package owns, and opens a `cloudflare-runtime`
26platform proxy (our wrangler-free `getPlatformProxy` equivalent) with the
27worker's bindings. The proxy's `{ env, cf, ctx }` is planted on
28`globalThis[Symbol.for("__cloudflare-context__")]` — the exact contract
29`@opennextjs/cloudflare`'s `getCloudflareContext()` reads — and
30`vm.runInContext` is patched so edge-runtime middleware/routes see it too.
31App code works without `initOpenNextCloudflareForDev()` and without wrangler.
32
33```ts
34import * as Nextjs from "@alchemy.run/frontend-frameworks/nextjs";
35
36const layer = Nextjs.make({
37 dev: { mode: "hmr" },
38 vite: {
39 compatibilityDate: "2026-08-31",
40 worker: { name: "my-app", bindings: [Text.local("TEST_TEXT", "value")] },
41 },
42});
43```
44
45#### Fidelity: what `"hmr"` mode is NOT
46
47`"hmr"` mode runs your app code in **Node.js** (and Next's edge-runtime VM
48sandbox for middleware/edge routes), not in workerd:
49
50- **Cloudflare-specific runtime behavior differs.** workerd APIs, runtime
51 limits, `nodejs_compat` shimming, `cloudflare:*` modules, request
52 lifecycles (`waitUntil` is a no-op), and the `cf` object (a static mock)
53 only behave faithfully in `"preview"` mode.
54- **Bindings round-trip over a local proxy.** `env.*` stubs forward method
55 chains to a workerd instance hosting the real local bindings. Method
56 results must be JSON-compatible values, bytes, dates, streams, or
57 `DurableObjectId`s; rich class returns (e.g. `R2Object`) and `connect()`
58 are not supported (see `cloudflare-runtime/platform-proxy`). Durable Object
59 classes defined in your app cannot run in this mode (the proxy worker does
60 not host your compiled code).
61- **ISR/caching semantics differ.** `next dev` never uses the OpenNext
62 incremental-cache/queue/tag-cache overrides — it uses Next's own dev cache.
63 Prerender/ISR behavior, `_next/image` via the `IMAGES` binding, and the
64 revalidation queue only exist in `"preview"` (and production).
65
66Rule of thumb: iterate with `"hmr"`, verify with `"preview"`.
67
68## Alchemy source provider
69
70`@alchemy.run/frontend-frameworks/nextjs/source` implements alchemy's `WorkerSourceModule`
71contract. The descriptor options (`NextjsSourceOptions`) are JSON-stable and
72include `dev.mode` to select the dev-server mode:
73
74```ts
75source: {
76 provider: "@alchemy.run/frontend-frameworks/nextjs/source",
77 options: { dev: { mode: "hmr" } },
78}
79```
80
81## Notes
82
83- The OpenNext pipeline runs `next build` internally via a child process
84 (`runner.mjs`); a `wrangler` stub package (`wrangler-stub/`) satisfies
85 OpenNext's version probe without installing wrangler.
86- `next` itself is resolved from the _project's_ `node_modules`, never from a
87 hoisted sibling.
88
89## Upstream references
90
91- Next.js: [`55d30fb91d8197597c5c400174f1d38a89333de8`](https://github.com/vercel/next.js/tree/55d30fb91d8197597c5c400174f1d38a89333de8)
92- OpenNext for Cloudflare: [`da4b7fc8c89441a6f96205635fa70e329d1f0df7`](https://github.com/opennextjs/opennextjs-cloudflare/tree/da4b7fc8c89441a6f96205635fa70e329d1f0df7)