How to Handle Hot Module Replacement with the Wrangler Dev Server in Next.js

To handle HMR with the Wrangler dev server, run the Next.js development server concurrently with Wrangler, allowing Next.js to manage UI hot-reloading while Wrangler provides the Cloudflare Workers runtime.

When building full-stack applications with the ifindev/fullstack-next-cloudflare template, you must navigate a specific architectural constraint: the Wrangler dev server does not support Hot Module Replacement (HMR) for Worker bundles. This article explains how to handle hot module replacement with the Wrangler dev server by leveraging a dual-server workflow that preserves instant UI updates while maintaining access to Cloudflare bindings like D1 and R2.

Why the Wrangler Dev Server Lacks HMR

Cloudflare Workers run in a specialized V8 isolate environment that differs significantly from Node.js. When you start the Wrangler dev server using wrangler dev, it compiles your Worker into a bundle and launches a local Miniflare runtime to simulate the Cloudflare Edge environment. Because this runtime loads a compiled bundle rather than raw source modules, it cannot swap individual modules without a full process restart. According to the repository's source code, this limitation is intrinsic to the Worker architecture—the Wrangler process serves the compiled output from .open-next/worker.js as specified in wrangler.jsonc, and no mechanism exists within wrangler dev to hot-swap this bundle.

The Dual-Server Development Workflow

The recommended approach to handle hot module replacement with the Wrangler dev server involves running two separate processes simultaneously. The package.json in the repository exposes distinct scripts for each responsibility:

"scripts": {
  "dev": "next dev",
  "wrangler:dev": "npx wrangler dev",
  "dev:cf": "npx @opennextjs/cloudflare build && wrangler dev",
  "dev:remote": "npx @opennextjs/cloudflare build && wrangler dev --remote"
}

Starting the Wrangler Dev Server

First, launch the Cloudflare Workers runtime to access local bindings. In ifindev/fullstack-next-cloudflare, execute:

pnpm run wrangler:dev

This command reads the configuration from wrangler.jsonc and starts the local runtime on http://localhost:8787. This server provides access to D1 databases, R2 buckets, and AI bindings, but does not support HMR. Any changes to API routes or Worker-specific code require stopping and restarting this process.

Starting the Next.js Dev Server

In a separate terminal, start the Next.js development server:

pnpm dev

This initiates the standard Next.js dev server with full HMR support. As implemented in next.config.ts, the template calls initOpenNextCloudflareForDev() when NODE_ENV equals "development":

import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare";

const nextConfig = {
  // Your Next.js configuration
};

if (process.env.NODE_ENV === "development") {
  initOpenNextCloudflareForDev();
}

export default nextConfig;

This function injects the same environment variables that Wrangler provides, allowing the Next.js server to communicate with local D1 or R2 instances directly while preserving React's fast refresh capabilities.

Configuring Next.js for Cloudflare Bindings

The initOpenNextCloudflareForDev() function is critical for bridging the two environments. By invoking this helper from @opennextjs/cloudflare inside next.config.ts, you enable the Next.js dev server to recognize Cloudflare-specific bindings during development. This means your frontend code can execute database queries against the local D1 instance managed by Wrangler without deploying to the Edge. Note that this configuration only applies to the development environment; production builds use the standard Worker bundle output.

Running Both Servers with a Single Command

If you prefer not to manage multiple terminal windows, you can automate the dual-server setup using concurrently. Add the following to your package.json:

"scripts": {
  "dev:all": "concurrently \"pnpm wrangler:dev\" \"pnpm dev\""
}

Then start both processes with:

pnpm run dev:all

Important: While this consolidates the startup process, the Wrangler dev server still requires a manual restart when you modify Worker code. HMR continues to function only for the Next.js side of the application.

Understanding HMR Boundaries

To develop efficiently, distinguish between what triggers a hot reload versus what requires a full restart:

  • Instant HMR (Next.js server): React components in src/app or src/components, CSS modules, client-side hooks, and page layouts update immediately in the browser.
  • Manual restart required (Wrangler server): API route handlers under src/app/api, Cloudflare binding configurations in wrangler.jsonc, environment variables, and any code that runs inside the Worker isolate.

When you modify a UI component, the Next.js server injects the updated module without refreshing the page. When you change an API route that uses the D1 database, you must stop and restart the wrangler:dev process to rebuild the Worker bundle.

Summary

  • No native HMR: The Wrangler dev server cannot hot-reload Worker bundles because it serves compiled JavaScript in a V8 isolate runtime.
  • Dual-server solution: Run pnpm wrangler:dev for the Cloudflare runtime and pnpm dev for Next.js with HMR.
  • Configuration bridge: The initOpenNextCloudflareForDev() function in next.config.ts enables the Next.js server to access local Cloudflare bindings during development.
  • Restart requirements: API route changes require restarting the Wrangler process, while UI updates reflect instantly via Next.js HMR.
  • Automation option: Use concurrently to launch both servers with a single command, though Wrangler still restarts manually when needed.

Frequently Asked Questions

Does the Wrangler dev server support HMR for Cloudflare Workers?

No. The Wrangler dev server serves a compiled Worker bundle and cannot hot-swap individual modules. It requires a full restart to load code changes, which is a limitation of the Cloudflare Workers runtime architecture as implemented in the local Miniflare environment.

Why do I need to run two servers for local development?

You need two servers because each handles a distinct responsibility. The Wrangler dev server provides the Cloudflare Workers runtime with access to D1, R2, AI, and other Edge bindings, while the Next.js dev server handles React compilation and provides HMR for the frontend. Running both concurrently allows you to develop with instant UI updates while testing against real Cloudflare APIs locally.

How do I access Cloudflare D1 or R2 during development with HMR?

Configure next.config.ts to call initOpenNextCloudflareForDev() when NODE_ENV is "development". This function bridges the Next.js dev server with the Wrangler runtime, allowing your React code to query local D1 databases or access R2 buckets directly while maintaining HMR for UI components.

Can I use a single command to start both Wrangler and Next.js dev servers?

Yes. Install the concurrently package and create a compound script in package.json such as "dev:all": "concurrently \"pnpm wrangler:dev\" \"pnpm dev\"". This starts both processes simultaneously, though you must still manually restart the Wrangler process when modifying Worker-specific code, as HMR remains unavailable for the Cloudflare runtime.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →