Vite’s Hot Module Replacement: Keep Your UI Fresh Without Reloading
Vite’s Hot Module Replacement (HMR) lets you edit components in real time, preserving state and avoiding full page reloads. This guide covers the workflow, key APIs, pitfalls, a counter example, performance trade‑offs, and next steps.
17 Aug 2026, 17:53 UTC

Why HMR Matters
Every time you tweak a component, the instinct is to hit Refresh and wait for the whole page to re‑render. In a large React tree that can cost minutes, especially when the app depends on data‑fetching or complex state. Vite’s Hot Module Replacement (HMR) turns that reload into a single, instant patch that keeps the UI in sync while preserving component state.
The HMR Workflow in Vite
When you run npm run dev (or vite directly), Vite starts a dev server that serves modules over HTTP. The browser establishes a WebSocket connection to the server. When you edit a file:
- Vite recompiles only the changed module.
- The server sends a hot‑update message over the socket.
- The browser receives the message, fetches the new module bundle, and applies it to the running app via the HMR API.
- Modules that export
import.meta.hotcan run custom accept callbacks to update state or perform cleanup.
Because only the changed module is re‑downloaded, the network traffic is minimal, and the browser never loses the current JavaScript heap, the UI updates instantly.
Key APIs and Configuration
Vite ships with a default HMR implementation that works out of the box for most frameworks. For React, the @vitejs/plugin-react package automatically injects HMR support. Your vite.config.js might look like this:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react({
// Enable fast refresh – the default is true
fastRefresh: true
})],
server: {
hmr: true // explicit, but true by default
}
})
When running the dev server, you can also enable verbose logging to see HMR events:
vite --debug
In the browser console you’ll see messages like HMR accepted when modules are patched.
Accepting and Declining Updates
Modules can opt‑in to HMR by exposing import.meta.hot:
if (import.meta.hot) {
import.meta.hot.accept((newModule) => {
// Replace the old component with the new one
App = newModule.default
})
}
Most framework plugins handle this automatically, but custom logic is useful for modules that need cleanup (e.g., event listeners).
Common Pitfalls and Debugging Tips
- Global side‑effects: If a component adds a global event listener on mount and never removes it, successive HMR cycles will accumulate listeners. Use
import.meta.hot.disposeto clean up:if (import.meta.hot) { import.meta.hot.dispose(() => { window.removeEventListener('resize', onResize) }) } - State loss: Some stateful logic (e.g., values stored outside React’s state) won’t survive HMR by default. Keep state inside React or use a store that preserves data across module replacements.
- Large bundles: In monorepos or very large apps, the initial HMR patch may still be sizeable. Split code into lazy‑loaded chunks or use Vite’s
build.rollupOptions.inputto control bundle size. - Framework overrides: If you’re using a framework that implements its own HMR (like SvelteKit), ensure the plugin is up‑to‑date; otherwise Vite’s defaults may be overridden.
Performance Considerations in Large Projects
While HMR saves time, it can increase memory usage because the browser keeps the old module in memory until the new one is fully applied. In projects with thousands of modules, this can lead to higher RAM consumption. Monitor the devtools Performance tab to spot memory spikes after frequent edits.
Another consideration is the number of HMR events. If a change triggers a cascade (e.g., a CSS update that forces a component to re‑render), the browser may process several patches in quick succession, potentially causing UI flicker. Vite’s HMR algorithm tries to coalesce updates, but extremely granular changes (like editing a single CSS rule in a large stylesheet) can still cause noticeable delays.
Worked Example: Counter Component That Persists State
- Start a fresh Vite + React project:
npm create vite@latest hmr-demo -- --template react cd hmr-demo npm install npm run dev - Open
src/App.jsxand replace its content with a simple counter:import { useState } from 'react' export default function App() { const [count, setCount] = useState(0) return ( HMR CounterCount: {count}
setCount(c => c + 1)}>Increment ) } - Run the app and click the button a few times. Note that
countis now 3. - Open the file in an editor and change the button text from
IncrementtoGrow. Save. - Observe the browser: the UI updates instantly, the counter stays at 3, and the console shows
HMR accepted. No full page reload occurred. - To verify that only the changed module was fetched, open the Network tab, filter by
app.js, and see that the file was fetched again with a200status. The rest of the bundle remains untouched.
Trade‑Offs and Limitations
- Memory Footprint – The dev server keeps the previous module versions in memory until the new ones are applied. In very large apps, this can lead to noticeable RAM usage.
- Complex Side‑Effects – Modules that register global listeners or timers without proper cleanup can cause leaks or duplicated behavior after an HMR cycle.
- Framework Specifics – Some frameworks (e.g., SvelteKit) override Vite’s HMR logic. Ensure the plugin version matches your framework’s expectations.
- Non‑JavaScript Resources – HMR only patches JavaScript modules. Changes to assets like CSS, images, or JSON files still trigger a full page reload unless the plugin handles them.
Next Steps
- Run
vite --debugto monitor HMR activity and catch errors early. - Use
import.meta.hot.disposein modules that create side‑effects to keep the app clean. - Consider splitting large components into lazy‑loaded chunks to reduce the size of HMR patches.
- In production builds, remember that HMR is disabled – ensure your build pipeline works correctly without it.
With Vite’s HMR, you can edit, save, and see instant UI changes without losing state, dramatically boosting developer velocity.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.