> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vertz.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# HMR Types (`import.meta.hot`)

> Type `import.meta.hot` in a Vertz app using the `vertz/client` augmentation.

`import.meta.hot` is typed as `ImportMetaHot | undefined` as soon as any file in your app imports from one of the client-runtime subpaths (`vertz/ui`, `vertz/ui/components`, `vertz/ui-primitives`, `vertz/ui-auth`). Their declarations carry a triple-slash reference that auto-loads the `vertz/client` augmentation.

## Explicit opt-in (optional)

If a file uses `import.meta.hot` without importing from any of those subpaths (e.g. a standalone client entry that only imports from `@vertz/ui` directly), wire the types up explicitly. Pick the subpath that matches what you install:

```jsonc theme={null}
// tsconfig.json — apps on the full framework
{
  "compilerOptions": {
    "types": ["vertz/client"],
  },
}
```

```jsonc theme={null}
// tsconfig.json — UI-only consumers (no `vertz` dependency)
{
  "compilerOptions": {
    "types": ["@vertz/ui/client"],
  },
}
```

Or, per-file:

```ts theme={null}
/// <reference types="vertz/client" />
// or: /// <reference types="@vertz/ui/client" />
import.meta.hot?.accept();
```

Both subpaths resolve to the same augmentation — `vertz/client` re-exports `@vertz/ui/client` via a triple-slash reference, so they cannot drift. Newly-scaffolded apps (via `create-vertz-app`) already include the `vertz/client` tsconfig entry.

## The `ImportMetaHot` surface

| Member                 | Description                                                                               |
| ---------------------- | ----------------------------------------------------------------------------------------- |
| `accept()`             | Self-accept — this module handles its own HMR updates.                                    |
| `accept(cb)`           | Self-accept with a callback receiving the new module.                                     |
| `accept(deps, cb?)`    | Accept updates for specific dependency paths.                                             |
| `dispose(cb)`          | Runs before this module is replaced. Receives a persistent `data` record.                 |
| `data`                 | `Record<string, unknown>` — persisted across HMR updates in the same session.             |
| `invalidate(message?)` | Mark this module as unable to apply an update; triggers a full reload with the message.   |
| `decline()`            | Opt out of HMR for this module. The next update targeting it falls back to a full reload. |
| `on(event, cb)`        | Subscribe to an HMR runtime event (see below).                                            |
| `off(event, cb)`       | Remove a previously-registered listener (must be the same callback reference).            |

## Why `hot` is `| undefined`

HMR is only active under `vtz dev`. Production builds and SSR have no `hot` object — so always optional-chain:

```ts theme={null}
import.meta.hot?.accept();
```

This is the same shape Vite uses and matches what the Bun build plugin actually emits (`if (import.meta.hot) import.meta.hot.accept();`).

## Narrowing `data`

`data` is typed `Record<string, unknown>` for safety. Cast at the use site rather than using an `any` type globally:

```ts theme={null}
import.meta.hot?.dispose((data) => {
  data.lastCount = 42;
});

const prev = import.meta.hot?.data.lastCount as number | undefined;
```

## Events

`hot.on(event, cb)` subscribes to runtime events. Each event delivers a typed payload:

| Event                    | Payload                                | When it fires                                                 |
| ------------------------ | -------------------------------------- | ------------------------------------------------------------- |
| `vertz:beforeUpdate`     | `{ module: string }`                   | Before a module is re-imported for an HMR update.             |
| `vertz:afterUpdate`      | `{ module: string }`                   | After a module has been re-imported and Fast Refresh has run. |
| `vertz:beforeFullReload` | `{ reason?: string }`                  | Before `location.reload()` is invoked (any cause).            |
| `vertz:invalidate`       | `{ module: string; message?: string }` | Another module called `hot.invalidate()`.                     |
| `vertz:error`            | `{ module: string; error: unknown }`   | An HMR update failed to apply.                                |

```ts theme={null}
import.meta.hot?.on('vertz:beforeUpdate', (p) => {
  console.log('about to update', p.module);
});
```

## Decline vs invalidate

Both fall back to a full page reload, but at different times:

* `decline()` marks a module as "do not try HMR." The next time the server pushes an update that includes this module, the client performs a full reload instead of a hot swap. Use when a module holds state that can't be re-created safely (e.g., a global WebSocket).
* `invalidate(message?)` triggers a full reload immediately. Use from inside an `accept()` callback when you discover at runtime that the new module can't take over (e.g., a new dependency version is incompatible with live state).

## Runtime behavior

Under `vtz dev`, the compiler rewrites `import.meta.hot` references to a runtime hot-context lookup. In production (no HMR runtime present) the lookup is undefined, so `import.meta.hot?.*` remains a no-op — safe in all environments.

## What's not here

`send()` (custom client → server events) and `prune()` (cleanup when a module is no longer imported anywhere) are not implemented yet. They require bidirectional WebSocket support and module-removal tracking respectively; tracked for a follow-up.
