I’ve always felt like HMR (Hot Module Replacement) is a bit of magic when you're in the zone. You change a line of code, hit save, and suddenly the change appears in the browser without a full refresh or losing your current state. It feels seamless.
But sometimes, the magic breaks. You’ve changed the code, but the UI doesn't change at all. Or even more annoying: the app just breaks because it's caught in a weird mix of old and new modules. In those moments, it feels like I'm chasing ghosts.
The truth is, HMR isn't magic. If you're using Vite, the logic is pretty technical: the server sends modules via native ESM, and when a change happens, the server tries to tell the browser exactly which part changed. If a module "accepts" that change, it just re-executes that specific component. If nothing can handle the change, the only option left is a full reload.
But HMR has its downsides too.
One of the most common issues is having mutable state at the module level. For example, if you define a variable outside of a component function that changes over time, HMR might not reset it the way you expect. This can also lead to stacking event listeners, meaning your functions get called twice or you end up with "ghost" subscriptions that make the app feel heavy.
Then there’s the issue of changing export shapes. If you rename a function or delete something that another file imports, HMR often gives up. Sometimes it triggers a full reload, but other times it just fails and throws confusing runtime errors—like "X is not a function"—even though the code looks perfectly fine.
Circular dependencies are another culprit. If module A imports module B, and module B imports module A, the update order becomes a mess. HMR might only partially update the module graph, leading to those weird errors that usually disappear once you do a hard reload.
Not everything should be forced through HMR, either. Changing a config file,
updating .env, or modifying app routes often won't be picked up by the
hot-swap system. It makes more sense to just reload manually rather than sitting
around waiting for a change that isn't coming.
One tip: if the app starts acting weird or doing things that don't make sense while you're just blasting ahead with coding, don't waste hours hunting for the bug. Try a hard reload first. If the problem is still there after the reload, it’s a genuine logic error in your code. But if the problem disappears and then pops up again the moment you save a file, you're likely dealing with a module-level state management issue or a circular dependency.
Back to the editor.