Vite + React: no HMR, page reloads on every change

I’ve been working on a project with Vite & React, using @jihchi/vite-plugin-rescript and @vitejs/plugin-react. I would’ve expected that if we’re able to leverage the standard Vite React plugin, we could benefit from HMR, but it doesn’t seem to be the case. The tiniest changes, eg. a change to a className, causes a full page reload.

Is there something about vite-plugin-rescript that conflicts with React HMR, or is there something I need to configure correctly?

I did a bit more research on this, and hit upon this caveat about React HMR:

The reason why it doesn’t work is that the additional export is not considered as a component (well, CamelCase functions/classes). If a file contains any exports that is not a component, the transform will not mark the component as side-effects free. This is important because anything else other than a component is generally not guaranteed to be side-effects free, but if there are side-effects, to have HMR work properly someone would need to work on cleaning up the side-effect, which is not possible cause what effect it is firing is unknown.

The way I understand this comment, the React plugin will mark modules with non-component exports as impure, and thus disables HMR when those files change. And to count as a component an export has to be both PascalCased and a function.

Could it be that fundamentally the compiled .bs.js subverts HMR by exporting either modules (a PascalCased object containing a make function), or a make function by itself (not PascalCased)? Do we then need to build in a Rescript-specific version of React HMR into vite-plugin-rescript?

For what it’s worth, have you tried to create interface files (*.resi) for each (*.res) file? Fast Refresh* requires you to only export React components.

*: Fast Refresh is enabled in development in @vitejs/plugin-react


I have now tried making a .resi file for my main component (Game.res). It doesn’t seem to help: when I make an edit, even to a single className, state is reset.

With the .resi file in place, the output .bs.js file ends with:

var make = Game;

export {
  make ,
/* Form Not a pure module */

As I already noted, I worry that make isn’t recognised as a component export because it isn’t PascalCased. Though I did experiment with editing the .bs.js file directly to export Game directly. That didn’t seem to help either.

I’m curious what the “not a pure module” comment is all about at the end. I don’t suppose it has any bearing on this issue?

Have you experienced Fast Refresh working in your own projects? Should I expect it to work, if I set things up right?

It’s definitely working with my setup but I remember there were some gotchas like correctly setting up include, that’s why I even created a small repo for a working setup.


Could you try to downgrade @vitejs/plugin-react to v4.0.1 and see if it helps?

I delved into the issue a bit, I believe the Fast Refresh stops working after you upgrade to @vitejs/plugin-react@v4.0.2 or above.

edited: Or, you could try to upgrade to @vitejs/plugin-react to v4.1.0 which includes the fix.


Sorry it’s taken me a while to get back to this, but it seems you’re right that 4.0.2 is the problem version. Downgrading to 4.0.1 but also upgrading to 4.1.0 works! (As long as I also lock down all my component files with resi)


Just to confirm for future people having this problem, it does need to be PascalCased, at least in the latest version as of this date.