> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/zhcndoc/bun/llms.txt
> Use this file to discover all available pages before exploring further.

# Hot Module Replacement (HMR)

> Fast development with Hot Module Replacement in Bun's bundler

Bun's bundler supports Hot Module Replacement (HMR) for fast development iteration without losing application state.

## What is HMR?

Hot Module Replacement (HMR) allows you to update modules in a running application without a full page reload. When you edit a file:

1. Bun detects the change
2. Rebuilds only the affected modules
3. Sends the update to the browser
4. The browser applies the update without refreshing

This preserves:

* Application state (form inputs, scroll position, etc.)
* React component state
* Global variables
* WebSocket connections

## Watch mode

Enable watch mode to rebuild automatically on file changes:

```bash theme={null}
bun build ./src/index.tsx --outdir ./dist --watch
```

This watches all files imported by your entry point and triggers a rebuild when any of them change.

## Development server

For full HMR support, use Bun's development server (coming soon):

```ts dev-server.ts theme={null}
const server = Bun.serve({
  port: 3000,
  
  async fetch(req) {
    const url = new URL(req.url);
    
    // Serve bundled assets with HMR
    if (url.pathname === "/app.js") {
      const result = await Bun.build({
        entrypoints: ["./src/index.tsx"],
        outdir: "./dist",
        watch: true,
      });
      
      return new Response(result.outputs[0]);
    }
    
    // Serve HTML
    return new Response(Bun.file("./index.html"));
  },
});

console.log(`Dev server running at http://localhost:${server.port}`);
```

## React Fast Refresh

React Fast Refresh is automatically enabled for `.jsx` and `.tsx` files:

```tsx Counter.tsx theme={null}
import { useState } from "react";

export function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
}
```

When you edit this component:

1. The component re-renders
2. State is preserved (count value remains)
3. No page reload occurs

Enable Fast Refresh explicitly:

```bash theme={null}
bun build ./src/index.tsx --outdir ./dist --react-fast-refresh
```

Or via the JavaScript API:

```ts theme={null}
await Bun.build({
  entrypoints: ["./src/index.tsx"],
  outdir: "./dist",
  reactFastRefresh: true,
});
```

## Module types

### Hot updates

Modules can opt into hot updates using the `import.meta.hot` API:

```ts theme={null}
if (import.meta.hot) {
  import.meta.hot.accept(() => {
    // Module was updated
    console.log("Module reloaded!");
  });
}
```

### Self-accepting modules

A module can handle its own updates:

```ts data.ts theme={null}
let data = loadData();

if (import.meta.hot) {
  import.meta.hot.accept(() => {
    // Reload data when this module updates
    data = loadData();
  });
}

export { data };
```

### Accepting dependencies

A module can handle updates to its dependencies:

```ts app.ts theme={null}
import { config } from "./config";

if (import.meta.hot) {
  import.meta.hot.accept("./config", (newConfig) => {
    // Config was updated
    console.log("Config updated:", newConfig);
  });
}
```

## CSS HMR

CSS updates are applied without a page reload:

```css styles.css theme={null}
.button {
  background: blue;
  color: white;
}
```

When you change the background color:

```css styles.css theme={null}
.button {
  background: red; /* Changed */
  color: white;
}
```

The style updates immediately in the browser without losing application state.

## Image HMR

Images are also hot-reloaded:

```tsx theme={null}
import logo from "./logo.png";

function App() {
  return <img src={logo} alt="Logo" />;
}
```

When you replace `logo.png`, the new image appears without a page reload.

## Configuration

### Debouncing

Watch mode debounces file changes to avoid rebuilding too frequently:

```ts theme={null}
await Bun.build({
  entrypoints: ["./src/index.tsx"],
  outdir: "./dist",
  watch: {
    // Wait 100ms after a file change before rebuilding
    debounce: 100,
  },
});
```

### Ignored paths

Exclude paths from watch mode:

```ts theme={null}
await Bun.build({
  entrypoints: ["./src/index.tsx"],
  outdir: "./dist",
  watch: {
    ignore: [
      "**/node_modules/**",
      "**/.git/**",
      "**/dist/**",
    ],
  },
});
```

## Error handling

When a build error occurs:

1. The error is displayed in the terminal
2. The browser shows an error overlay (if using a dev server)
3. The previous working code continues to run
4. When you fix the error, HMR resumes

## Performance

HMR in Bun is fast:

* Incremental rebuilds (only changed modules)
* Parallel processing
* Minimal browser updates (only changed code)
* No disk I/O for small changes

## Examples

### React application

```tsx App.tsx theme={null}
import { useState } from "react";
import "./App.css";

export function App() {
  const [count, setCount] = useState(0);
  
  return (
    <div className="app">
      <h1>Counter: {count}</h1>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
}
```

```ts index.tsx theme={null}
import { createRoot } from "react-dom/client";
import { App } from "./App";

const root = createRoot(document.getElementById("root")!);
root.render(<App />);
```

```bash theme={null}
bun build ./src/index.tsx --outdir ./dist --watch --react-fast-refresh
```

### Vanilla JavaScript

```ts app.ts theme={null}
import { render } from "./render";
import "./styles.css";

render();

if (import.meta.hot) {
  import.meta.hot.accept("./render", (newRender) => {
    newRender();
  });
}
```

### State management

```ts store.ts theme={null}
class Store {
  state = { count: 0 };
  
  increment() {
    this.state.count++;
  }
}

const store = new Store();

if (import.meta.hot) {
  // Preserve store state across HMR updates
  if (import.meta.hot.data.store) {
    Object.assign(store, import.meta.hot.data.store);
  }
  
  import.meta.hot.dispose((data) => {
    data.store = store;
  });
}

export { store };
```

## Limitations

* HMR requires a development server (static files don't support HMR)
* Not all changes can be hot-reloaded (e.g., changing module exports)
* Some state might be lost (e.g., closures, module-level variables)
* Full page reload is needed for:
  * HTML changes
  * Adding/removing files
  * Changes to build configuration
  * Changes to imported npm packages

## Best practices

### Keep state in React

Store application state in React components rather than module-level variables:

```tsx theme={null}
// Good: State in component
export function Counter() {
  const [count, setCount] = useState(0);
  return <div>{count}</div>;
}

// Bad: Module-level state (lost on HMR)
let count = 0;
export function Counter() {
  return <div>{count}</div>;
}
```

### Use HMR API for cleanup

Clean up side effects when a module is replaced:

```ts theme={null}
const interval = setInterval(() => {
  console.log("Tick");
}, 1000);

if (import.meta.hot) {
  import.meta.hot.dispose(() => {
    clearInterval(interval);
  });
}
```

### Organize for HMR

Split code into small modules that can be updated independently:

```ts theme={null}
// Good: Small, focused modules
import { Button } from "./Button";
import { Input } from "./Input";

// Bad: Large modules that change frequently
import { Button, Input, Modal, Dropdown } from "./components";
```

## Troubleshooting

### Changes not detected

Make sure the file is imported by your entry point:

```ts theme={null}
// Entry point must import changed files
import "./styles.css"; // This file will be watched
```

### Full reload on every change

Check if your modules are self-accepting:

```ts theme={null}
if (import.meta.hot) {
  import.meta.hot.accept(); // Add this
}
```

### State lost on update

Use React Fast Refresh for components or implement state preservation:

```ts theme={null}
if (import.meta.hot) {
  import.meta.hot.dispose((data) => {
    data.myState = currentState;
  });
  
  import.meta.hot.accept(() => {
    if (import.meta.hot.data.myState) {
      restoreState(import.meta.hot.data.myState);
    }
  });
}
```
