HMR API — import.meta.hot
The per-module Hot Module Replacement API. It's present only in dev — always guard usage with if (import.meta.hot) so the block tree-shakes out of the production build. The API mirrors the widely-used shape, so framework integrations and plugins target a familiar surface.
For the conceptual walkthrough, see the HMR guide. This page is the reference.
if (import.meta.hot) {
import.meta.hot.accept((mod) => {
// re-run with the updated module
});
}Types come from vantris/client.
accept
Mark this module (or specific dependencies) as an update boundary.
// Self-accept, no callback: re-execute this module on change.
accept(): void;
// Self-accept with the updated module namespace.
accept(cb: (mod: ModuleNamespace | undefined) => void): void;
// Accept updates from a single dependency.
accept(dep: string, cb: (mod: ModuleNamespace | undefined) => void): void;
// Accept updates from several dependencies.
accept(deps: readonly string[], cb: (mods: Array<ModuleNamespace | undefined>) => void): void;ModuleNamespace is the re-imported module's exports (or undefined if it failed to load).
Self-accept:
export function render() { /* … */ }
render();
if (import.meta.hot) {
import.meta.hot.accept((mod) => mod?.render());
}Accept a dependency:
import { config } from "./config.ts";
if (import.meta.hot) {
import.meta.hot.accept("./config.ts", (newConfig) => {
applyConfig(newConfig?.config);
});
}dispose
Run before the module is replaced — tear down timers, listeners, and other side effects created by the current version. The callback receives the module's persistent data object.
dispose(cb: (data: Record<string, unknown>) => void): void;const timer = setInterval(tick, 1000);
if (import.meta.hot) {
import.meta.hot.dispose(() => clearInterval(timer));
}prune
Run when the module is removed from the graph (no longer imported by anything). Use it for cleanup that should happen on removal, not on every update.
prune(cb: (data: Record<string, unknown>) => void): void;invalidate
Give up on HMR for this update and reload the page. Call it when the module can't safely apply an in-place update. The optional message is logged.
invalidate(message?: string): void;if (import.meta.hot) {
import.meta.hot.accept((mod) => {
if (!mod) import.meta.hot!.invalidate("failed to re-import");
});
}decline
Opt this module out of HMR entirely — any change to it always triggers a full reload.
decline(): void;data
An object that persists across updates of this module. Write to it in dispose to hand state to the next version, and read from it at module top level to restore.
readonly data: Record<string, unknown>;if (import.meta.hot) {
let count = (import.meta.hot.data.count as number) ?? 0;
import.meta.hot.dispose((data) => { data.count = count; });
}Custom events — on / off / send
A two-way channel shared with the dev server and any server-side plugin.
on(event: string, cb: (data: unknown) => void): void;
off(event: string, cb: (data: unknown) => void): void;
send(event: string, data?: unknown): void;Listen for an event pushed by a plugin's handleHotUpdate or configureServer:
if (import.meta.hot) {
import.meta.hot.on("data:changed", ({ file }) => refetch(file));
}Send an event back to the server:
if (import.meta.hot) {
import.meta.hot.send("client:ready", { at: Date.now() });
}The full interface
interface VantrisHotContext {
readonly data: Record<string, unknown>;
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: Record<string, unknown>) => void): void;
prune(cb: (data: Record<string, unknown>) => void): void;
invalidate(message?: string): void;
decline(): void;
on(event: string, cb: (data: unknown) => void): void;
off(event: string, cb: (data: unknown) => void): void;
send(event: string, data?: unknown): void;
}Server-side HMR types
When writing a plugin's handleHotUpdate, you work with the server-side HMR types, exported from vantris:
HotUpdateContext—file, the affectedmodules, and aserverhandle (send, …).ModuleNode/ModuleInfo— nodes in the server module graph.ModuleGraph— the graph of importer edges and accept boundaries.HmrEngine— turns a change intoupdate/reload/prunepayloads.HmrPayload/ClientMessage/Update— the wire protocol types.
import type { HotUpdateContext, ModuleNode } from "vantris";See the JavaScript API → HMR and the Plugin API → handleHotUpdate.
Production behaviour
import.meta.hot is undefined in a production build. Because you guard every use with if (import.meta.hot), the guarded code is statically dead and removed by tree shaking — none of your HMR wiring ships to users.