| name | Bun Hot Reloading |
| description | Use when implementing hot reloading with Bun (--hot, --watch), HMR, or automatic code reloading during development. Covers watch mode, hot mode, and HTTP server reload. |
| version | 1.0.0 |
Bun Hot Reloading
Bun provides built-in hot reloading for faster development cycles.
Watch Mode vs Hot Mode
| Feature | --watch | --hot |
|---|
| Behavior | Restart process | Reload modules |
| State | Lost on reload | Preserved |
| Speed | ~20ms restart | Instant reload |
| Use case | Any file type | Bun.serve HTTP |
Watch Mode (--watch)
Restarts the entire process when files change.
bun --watch run src/index.ts
bun --watch run dev
bun --watch test
package.json Scripts
{
"scripts": {
"dev": "bun --watch run src/index.ts",
"dev:server": "bun --watch run src/server.ts",
"test:watch": "bun --watch test"
}
}
Watch Behavior
- Watches imported files automatically
- Triggers on any
.ts, .tsx, .js, .jsx change
- Also watches
.json imports
- Restarts with fresh state
Hot Mode (--hot)
Reloads modules in-place without restarting the process.
bun --hot run src/server.ts
HTTP Server Hot Reload
let counter = 0;
export default {
port: 3000,
fetch(req: Request) {
counter++;
return new Response(`Request #${counter}`);
},
};
bun --hot run src/server.ts
When you modify server.ts, the module reloads instantly while counter keeps its value.
Bun.serve with Hot Reload
const server = Bun.serve({
port: 3000,
fetch(req) {
return new Response("Hello!");
},
});
if (import.meta.hot) {
import.meta.hot.accept(() => {
console.log("Hot reload!");
});
}
console.log(`Server running on port ${server.port}`);
import.meta.hot API
if (import.meta.hot) {
import.meta.hot.accept();
import.meta.hot.accept((newModule) => {
console.log("Module updated:", newModule);
});
import.meta.hot.dispose(() => {
clearInterval(myInterval);
});
import.meta.hot.decline();
import.meta.hot.invalidate();
}
HTTP Server Patterns
Express-like Pattern
import { createApp } from "./app";
const app = createApp();
const server = Bun.serve({
port: 3000,
fetch: app.fetch,
});
if (import.meta.hot) {
import.meta.hot.accept((newModule) => {
server.reload({
fetch: newModule.default.fetch,
});
});
}
Stateful Server
globalThis.connections ??= new Set();
const server = Bun.serve({
port: 3000,
fetch(req) {
return new Response(`Connections: ${globalThis.connections.size}`);
},
websocket: {
open(ws) {
globalThis.connections.add(ws);
},
close(ws) {
globalThis.connections.delete(ws);
},
},
});
if (import.meta.hot) {
import.meta.hot.accept();
}
Custom Watch Implementation
import { watch } from "fs";
const srcDir = "./src";
let server: ReturnType<typeof Bun.serve> | null = null;
async function startServer() {
const module = await import(`./src/server.ts?t=${Date.now()}`);
if (server) {
server.stop();
}
server = Bun.serve(module.default);
console.log(`Server started on port ${server.port}`);
}
await startServer();
watch(srcDir, { recursive: true }, async (event, filename) => {
if (filename?.endsWith(".ts") || filename?.endsWith(".tsx")) {
console.log(`\n[${event}] ${filename}`);
await startServer();
}
});
console.log("Watching for changes...");
WebSocket Live Reload
Server
const clients = new Set<ServerWebSocket>();
const server = Bun.serve({
port: 3000,
fetch(req, server) {
if (req.headers.get("upgrade") === "websocket") {
server.upgrade(req);
return;
}
const html = `
<!DOCTYPE html>
<html>
<body>
<h1>Hello!</h1>
<script>
const ws = new WebSocket('ws://localhost:3000');
ws.onmessage = (e) => {
if (e.data === 'reload') location.reload();
};
</script>
</body>
</html>
`;
return new Response(html, {
headers: { "Content-Type": "text/html" },
});
},
websocket: {
open(ws) {
clients.add(ws);
},
close(ws) {
clients.delete(ws);
},
},
});
watch("./src", { recursive: true }, () => {
clients.forEach((ws) => ws.send("reload"));
});
Vite Integration
For frontend development with HMR:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
server: {
port: 5173,
hmr: true,
},
});
bunx --bun vite
Testing with Watch
bun --watch test
bun --watch test src/utils.test.ts
bun --watch test --bail
Environment Detection
const isHot = !!import.meta.hot;
const isWatch = process.env.BUN_WATCH === "1";
const isDev = process.env.NODE_ENV !== "production";
if (isDev) {
console.log("Running in development mode");
console.log(`Hot reload: ${isHot}`);
console.log(`Watch mode: ${isWatch}`);
}
Common Issues
State Not Preserved
let cache = new Map();
globalThis.cache ??= new Map();
const cache = globalThis.cache;
Cleanup Not Running
setInterval(() => console.log("tick"), 1000);
const interval = setInterval(() => console.log("tick"), 1000);
if (import.meta.hot) {
import.meta.hot.dispose(() => {
clearInterval(interval);
});
}
Module Not Reloading
const config = require("./config.json");
import config from "./config.json";
Common Errors
| Error | Cause | Fix |
|---|
Changes not detected | File not imported | Check import chain |
State lost | Using --watch | Use --hot or globalThis |
Port in use | Server not stopped | Implement server.stop() |
Memory leak | No cleanup | Use dispose callback |
When to Load References
Load references/advanced-hmr.md when:
- Custom HMR protocols
- Module federation
- Complex state management
Load references/debugging.md when:
- HMR not working
- State issues
- Performance debugging