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

# HMR API

> Client-side Hot Module Replacement API for Vite

<Note>
  This is the client HMR API. For handling HMR update in plugins, see [handleHotUpdate](./api-plugin#handlehotupdate).

  The manual HMR API is primarily intended for framework and tooling authors. As an end user, HMR is likely already handled for you in the framework specific starter templates.
</Note>

Vite exposes its manual HMR API via the special `import.meta.hot` object:

## TypeScript Interface

```ts theme={null}
interface ImportMeta {
  readonly hot?: ViteHotContext
}

interface ViteHotContext {
  readonly data: any

  accept(): void
  accept(cb: (mod: ModuleNamespace | undefined) => void): void
  accept(dep: string, cb: (mod: ModuleNamespace | undefined) => void): void
  accept(
    deps: readonly string[],
    cb: (mods: Array<ModuleNamespace | undefined>) => void,
  ): void

  dispose(cb: (data: any) => void): void
  prune(cb: (data: any) => void): void
  invalidate(message?: string): void

  on<T extends CustomEventName>(
    event: T,
    cb: (payload: InferCustomEventPayload<T>) => void,
  ): void
  off<T extends CustomEventName>(
    event: T,
    cb: (payload: InferCustomEventPayload<T>) => void,
  ): void
  send<T extends CustomEventName>(
    event: T,
    data?: InferCustomEventPayload<T>,
  ): void
}
```

## Required Conditional Guard

First of all, make sure to guard all HMR API usage with a conditional block so that the code can be tree-shaken in production:

```js theme={null}
if (import.meta.hot) {
  // HMR code
}
```

## IntelliSense for TypeScript

Vite provides type definitions for `import.meta.hot` in [`vite/client.d.ts`](https://github.com/vitejs/vite/blob/main/packages/vite/client.d.ts). You can add "vite/client" in the `tsconfig.json` so TypeScript picks up the type definitions:

```json theme={null}
{
  "compilerOptions": {
    "types": ["vite/client"]
  }
}
```

## hot.accept()

For a module to self-accept, use `import.meta.hot.accept` with a callback which receives the updated module.

### Type Signature

```ts theme={null}
accept(cb: (mod: ModuleNamespace | undefined) => void): void
```

### Parameters

<ParamField path="cb" type="(mod: ModuleNamespace | undefined) => void" required>
  Callback function that receives the updated module. The module is `undefined` when a SyntaxError happened.
</ParamField>

### Example

```js theme={null}
export const count = 1

if (import.meta.hot) {
  import.meta.hot.accept((newModule) => {
    if (newModule) {
      // newModule is undefined when SyntaxError happened
      console.log('updated: count is now ', newModule.count)
    }
  })
}
```

A module that "accepts" hot updates is considered an **HMR boundary**.

<Note>
  Vite's HMR does not actually swap the originally imported module: if an HMR boundary module re-exports imports from a dep, then it is responsible for updating those re-exports (and these exports must be using `let`). In addition, importers up the chain from the boundary module will not be notified of the change.

  This simplified HMR implementation is sufficient for most dev use cases, while allowing us to skip the expensive work of generating proxy modules.
</Note>

<Warning>
  Vite requires that the call to this function appears as `import.meta.hot.accept(` (whitespace-sensitive) in the source code in order for the module to accept update. This is a requirement of the static analysis that Vite does to enable HMR support for a module.
</Warning>

## hot.accept(deps, cb)

A module can also accept updates from direct dependencies without reloading itself.

### Type Signature

```ts theme={null}
accept(dep: string, cb: (mod: ModuleNamespace | undefined) => void): void
accept(
  deps: readonly string[],
  cb: (mods: Array<ModuleNamespace | undefined>) => void,
): void
```

### Parameters

<ParamField path="dep" type="string" required>
  Single dependency path to accept updates from
</ParamField>

<ParamField path="deps" type="readonly string[]" required>
  Array of dependency paths to accept updates from
</ParamField>

<ParamField path="cb" type="function" required>
  Callback that receives the updated module(s). For single dependency, receives one module. For multiple dependencies, receives an array where only the updated module is non-null.
</ParamField>

### Example

```js theme={null}
import { foo } from './foo.js'

foo()

if (import.meta.hot) {
  // Accept single dependency
  import.meta.hot.accept('./foo.js', (newFoo) => {
    // the callback receives the updated './foo.js' module
    newFoo?.foo()
  })

  // Accept array of dependencies
  import.meta.hot.accept(
    ['./foo.js', './bar.js'],
    ([newFooModule, newBarModule]) => {
      // The callback receives an array where only the updated module is
      // non null. If the update was not successful (syntax error for ex.),
      // the array is empty
    },
  )
}
```

## hot.dispose()

A self-accepting module or a module that expects to be accepted by others can use `hot.dispose` to clean-up any persistent side effects created by its updated copy.

### Type Signature

```ts theme={null}
dispose(cb: (data: any) => void): void
```

### Parameters

<ParamField path="cb" type="(data: any) => void" required>
  Callback function for cleanup. Receives the `data` object which can be used to pass information to the new module instance.
</ParamField>

### Example

```js theme={null}
function setupSideEffect() {}

setupSideEffect()

if (import.meta.hot) {
  import.meta.hot.dispose((data) => {
    // cleanup side effect
  })
}
```

## hot.prune()

Register a callback that will be called when the module is no longer imported on the page.

### Type Signature

```ts theme={null}
prune(cb: (data: any) => void): void
```

### Parameters

<ParamField path="cb" type="(data: any) => void" required>
  Callback function for cleanup when module is removed from the page
</ParamField>

Compared to `hot.dispose`, this can be used if the source code cleans up side-effects by itself on updates and you only need to clean-up when it's removed from the page. Vite currently uses this for `.css` imports.

### Example

```js theme={null}
function setupOrReuseSideEffect() {}

setupOrReuseSideEffect()

if (import.meta.hot) {
  import.meta.hot.prune((data) => {
    // cleanup side effect
  })
}
```

## hot.data

The `import.meta.hot.data` object is persisted across different instances of the same updated module. It can be used to pass on information from a previous version of the module to the next one.

### Type

```ts theme={null}
readonly data: any
```

<Note>
  Re-assignment of `data` itself is not supported. Instead, you should mutate properties of the `data` object so information added from other handlers are preserved.
</Note>

### Example

```js theme={null}
// ok
import.meta.hot.data.someValue = 'hello'

// not supported
import.meta.hot.data = { someValue: 'hello' }
```

## hot.decline()

This is currently a noop and is there for backward compatibility. This could change in the future if there is a new usage for it. To indicate that the module is not hot-updatable, use `hot.invalidate()`.

## hot.invalidate()

A self-accepting module may realize during runtime that it can't handle a HMR update, and so the update needs to be forcefully propagated to importers.

### Type Signature

```ts theme={null}
invalidate(message?: string): void
```

### Parameters

<ParamField path="message" type="string" optional>
  Optional message to give context on why the invalidation happened
</ParamField>

By calling `import.meta.hot.invalidate()`, the HMR server will invalidate the importers of the caller, as if the caller wasn't self-accepting. This will log a message both in the browser console and in the terminal.

<Note>
  You should always call `import.meta.hot.accept` even if you plan to call `invalidate` immediately afterwards, or else the HMR client won't listen for future changes to the self-accepting module. To communicate your intent clearly, we recommend calling `invalidate` within the `accept` callback.
</Note>

### Example

```js theme={null}
import.meta.hot.accept((module) => {
  // You may use the new module instance to decide whether to invalidate.
  if (cannotHandleUpdate(module)) {
    import.meta.hot.invalidate()
  }
})
```

## hot.on()

Listen to an HMR event.

### Type Signature

```ts theme={null}
on<T extends CustomEventName>(
  event: T,
  cb: (payload: InferCustomEventPayload<T>) => void,
): void
```

### Parameters

<ParamField path="event" type="string" required>
  The event name to listen for
</ParamField>

<ParamField path="cb" type="(payload: any) => void" required>
  Callback function that receives the event payload
</ParamField>

### Built-in Events

The following HMR events are dispatched by Vite automatically:

* `'vite:beforeUpdate'` - when an update is about to be applied (e.g. a module will be replaced)
* `'vite:afterUpdate'` - when an update has just been applied (e.g. a module has been replaced)
* `'vite:beforeFullReload'` - when a full reload is about to occur
* `'vite:beforePrune'` - when modules that are no longer needed are about to be pruned
* `'vite:invalidate'` - when a module is invalidated with `import.meta.hot.invalidate()`
* `'vite:error'` - when an error occurs (e.g. syntax error)
* `'vite:ws:disconnect'` - when the WebSocket connection is lost
* `'vite:ws:connect'` - when the WebSocket connection is (re-)established

Custom HMR events can also be sent from plugins. See [handleHotUpdate](./api-plugin#handlehotupdate) for more details.

### Example

```js theme={null}
if (import.meta.hot) {
  import.meta.hot.on('vite:beforeUpdate', (payload) => {
    console.log('Update incoming:', payload)
  })
}
```

## hot.off()

Remove callback from the event listeners.

### Type Signature

```ts theme={null}
off<T extends CustomEventName>(
  event: T,
  cb: (payload: InferCustomEventPayload<T>) => void,
): void
```

### Parameters

<ParamField path="event" type="string" required>
  The event name to remove the listener from
</ParamField>

<ParamField path="cb" type="(payload: any) => void" required>
  The callback function to remove (must be the same reference as used in `on()`)
</ParamField>

## hot.send()

Send custom events back to Vite's dev server.

### Type Signature

```ts theme={null}
send<T extends CustomEventName>(
  event: T,
  data?: InferCustomEventPayload<T>,
): void
```

### Parameters

<ParamField path="event" type="string" required>
  Custom event name
</ParamField>

<ParamField path="data" type="any" optional>
  Optional data payload to send with the event
</ParamField>

If called before connected, the data will be buffered and sent once the connection is established.

See [Client-server Communication](/guide/api-plugin.html#client-server-communication) for more details, including a section on [Typing Custom Events](/guide/api-plugin.html#typescript-for-custom-events).

## Further Reading

If you'd like to learn more about how to use the HMR API and how it works under-the-hood, check out these resources:

* [Hot Module Replacement is Easy](https://bjornlu.com/blog/hot-module-replacement-is-easy)
