localhost:5173 / vite-hmr
Hot Module Replacement on localhost:5173
HMR swaps a changed module in the running page over a WebSocket instead of reloading it; when that WebSocket cannot connect, Vite falls back to full page reloads or stops updating at all.
What actually happens
Vite watches your files. On a change it sends the new module over a WebSocket to the browser, where it replaces the old one in place — component state survives, scroll position survives, the page does not reload. A full reload means that handoff failed somewhere.
The deprecated option almost every answer still shows
The WebSocket settings under server.hmr — protocol, host, port, path, clientPort, timeout, server — are deprecated. They live under server.ws now. The old keys are still synced automatically so existing configs keep working, which is exactly why the outdated advice never stopped circulating.
// deprecated server: { hmr: { clientPort: 443 } } // current server: { ws: { clientPort: 443 } }
Behind a reverse proxy or tunnel
The browser loads the page from your public hostname on port 443, then tries to open the WebSocket on the port Vite thinks it is on. clientPort tells the client where to connect instead.
export default defineConfig({
server: {
ws: {
protocol: 'wss',
clientPort: 443,
},
},
})The fallback message in the console
If the proxy in front of Vite does not forward WebSocket connections, the client gives up on the proxied route and connects to the HMR server directly. It prints a connection error first and then works anyway.
Direct websocket connection fallback. Check out https://vite.dev/config/server-options.html#server-ws to remove the previous connection error.
server.hmr today
What remains under server.hmr is the behaviour itself: false turns HMR off, and overlay: false hides the full-screen error overlay without touching the updates.
server: {
hmr: { overlay: false },
}Nothing updates at all
Two usual causes, both outside Vite. On a bind-mounted volume in Docker or on some network drives, filesystem events never reach the watcher — polling is the workaround. And a module that does not accept an update propagates upward until something does; if nothing does, Vite reloads the page. That is not a bug, that is the fallback working.
server: {
watch: { usePolling: true }, // only when events do not arrive
}Turning it off is a cost
usePolling asks the filesystem for changes on a timer instead of being told. On a large project it keeps a core busy. Use it where events genuinely do not arrive, not as a default.
# faq
Questions
What does HMR stand for?
Hot Module Replacement — replacing a changed module in the running page without reloading it.
Why does my page do a full reload instead of hot updating?
Either the WebSocket did not connect, or no module in the chain accepted the update, so Vite fell back to reloading.
Is server.hmr.clientPort still correct?
It still works but is deprecated. The current location is server.ws.clientPort.
Why does HMR not work in Docker?
File change events often do not cross a bind mount. server.watch.usePolling is the usual workaround, at the cost of CPU.
# next
Related
# sources
Checked against the Vite documentation on 2026-09-11.