Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vite: Styles lost during HMR mode when using Client Data/SPA Mode #8466

Closed
brophdawg11 opened this issue Jan 9, 2024 · 18 comments
Closed

Vite: Styles lost during HMR mode when using Client Data/SPA Mode #8466

brophdawg11 opened this issue Jan 9, 2024 · 18 comments
Labels
bug Something isn't working external vite

Comments

@brophdawg11
Copy link
Contributor

Reproduction

Use a side effect import in root to load styles
Add a clientLoader and HydrateFallback to root so it SSR's a fallback
Load / and see styles only exist on HydrateFallback's render but are lost when we flip over to root

System Info

System:
    OS: macOS 14.2.1
    CPU: (8) arm64 Apple M1 Pro
    Memory: 122.72 MB / 16.00 GB
    Shell: 3.2.57 - /bin/bash
  Binaries:
    Node: 18.17.1 - ~/n/bin/node
    Yarn: 1.22.19 - ~/n/bin/yarn
    npm: 9.6.7 - ~/n/bin/npm
    pnpm: 7.30.5 - ~/n/bin/pnpm
  Browsers:
    Chrome: 120.0.6099.216
    Edge: 120.0.2210.121
    Safari: 17.2.1
  npmPackages:
    @remix-run/changelog-github: ^0.0.5 => 0.0.5
    @remix-run/web-fetch: ^4.4.2 => 4.4.2
    vite: ^5.0.0 => 5.0.0

Used Package Manager

npm

Expected Behavior

Styles persist on load and on subsequent HMR

Actual Behavior

Styles lost on load (and during any subsequent HMR)

@brophdawg11 brophdawg11 self-assigned this Jan 9, 2024
@brophdawg11 brophdawg11 added bug Something isn't working vite and removed bug:unverified labels Jan 9, 2024
@Danones
Copy link

Danones commented Jan 10, 2024

@brophdawg11

With the information you provided here, I am not 100% sure if this is the same root issue but please have a look at the following issues:

#4822 (comment)

facebook/react#24430

It seems like this is related to the DOM being manipulated after rendering the content in the page. (oversimplified explanation)
Right now this seems to be an issue that React team is aware and also Remix but this might be good to be raised with the Vite team;
I have also opened my self an issue here

@brophdawg11
Copy link
Contributor Author

There are no hydration issues involved with this issue so I don't think those are quite the same cause

@Danones
Copy link

Danones commented Jan 10, 2024

Ok it was worth a try :)

@brophdawg11
Copy link
Contributor Author

Welp, it turns out you were (at least partially) right! Even though this issue doesn't have hydration errors - it seems the root cause is something to do with how React handles updates to the <head> (paired with @pcattori - he and @markdalgleish have already seem similar issues with root ErrorBoundary components) . The issue is resolved when using react version 18.3.0-canary-08cd087ca-20240110.

The issue was tracked down to the following scenario (happens in both Remix + Client Data and Remix SPA mode):

  • You are using Remix + Vite
  • You have a side-effect css import in root.tsx
  • You have a HydrateFallback on root.tsx being SSR'd
  • Once the client data loads, root.tsx switches over to the default root.tsx component
  • This has a different <head> JSX element
  • This causes vite <style> tags inserted in the <head> during HMR to get lost

For now, the workaround would be:

  • use react@canary
  • If using Remix SSR + Client Data, you could move the HydrateFallback down below the root
  • If using Remix SPA mode, avoid side-effect CSS imports in root.tsx

Also note that this is only a dev-time HMR problem in vite and side-effect imports in root.tsx should work correctly when doing npm remix build.

@Danones
Copy link

Danones commented Jan 10, 2024

Glad I was helpful or at least pointed you to the right place. 👍🏽

@avinashbot
Copy link

Trying out SPA mode and running into this issue. Wanted to mention that this even happens when there are side-effect CSS imports outside of root.tsx (e.g. inside routes). Reproduction here.

@archywillhe
Copy link

archywillhe commented Jan 12, 2024

I'm debugging this issue too; anyone can point me to the files that implements the spa pipeline ? thanks

@markdalgleish
Copy link
Member

I've added some docs to the Remix + Vite troubleshooting guide covering this issue, along with a couple of ways to fix it: https://remix.run/docs/en/main/future/vite#styles-disappearing-in-development-when-document-remounts

@avinashbot
Copy link

@markdalgleish If I understand the guide correctly, is removing the HydrateFallback/ErrorBoundary on root all it takes to make HMR work nicely again? I just tried it and it doesn't seem to be working (a fork without a HydrateFallback in case I'm doing something wrong). But I can confirm that it's fixed on canary releases of React.

And regarding package.json overrides, I don't think npm allows you to override packages that you directly depend on. It certainly didn't work for me, and the docs seem to confirm that:

You may not set an override for a package that you directly depend on unless both the dependency and the override itself share the exact same spec.

@markdalgleish
Copy link
Member

@avinashbot Nice catch with the overrides, I've fixed the example on the site. I'll double check the other point you raised tomorrow.

@brophdawg11
Copy link
Contributor Author

@avinashbot Removing the HydrateFallback as a workaround won't quite work right in SPA mode because SPA Mode only renders the root route when generating index.html and will use an internally-defined HydrateFallback if you don't provide one. Using a canary is likely the best solution there for now (assuming the overrides work).

@markdalgleish
Copy link
Member

I've updated the docs to focus on canaries as the solution to this problem since I missed some nuances around HydrateFallback.

@brophdawg11 brophdawg11 removed their assignment Jan 17, 2024
@Abbbdab
Copy link

Abbbdab commented Jan 19, 2024

@markdalgleish @brophdawg11 are you guys planning to ship Remix with 18.3 canary by default in next releases?

@brophdawg11
Copy link
Contributor Author

No - you are in charge of the react version for your app, not us

@pcattori
Copy link
Contributor

pcattori commented Feb 5, 2024

Closing this as there's nothing further that Remix can do about it. As shared by Mark, we've documented that the fix is included as part of React canary.

@pcattori pcattori closed this as completed Feb 5, 2024
@shmuli9
Copy link

shmuli9 commented Jun 3, 2024

Just wanted to add: I noticed hydration issues relating to browser plugins (as determined by lack of console warning in an Incognito browser with most plugins disabled - only Adblock and my pass manager are enabled in incognito so clearly none of them are the cause)

Based on some of the above chatter, i thought this might be causing the styling reload issues... lo and behold, hot reload works perfectly in incognito. Hope this is helpful for someone else...

Update: disabling Urban VPN extension fixes the issue for me (side note: what in the heck is that doing injecting code onto my page???)

style.reload.issue.mp4

@allicanseenow
Copy link

I wonder why this issue was closed while the issue facebook/react#24430 is not?

And is the doc outdated here as the canary version is now react 19 instead? Should it be updated to react@rc react-dom@rc?

@grinkus-adapt
Copy link

grinkus-adapt commented Nov 5, 2024

Just wanted to add: I noticed hydration issues relating to browser plugins (as determined by lack of console warning in an Incognito browser with most plugins disabled - only Adblock and my pass manager are enabled in incognito so clearly none of them are the cause)

Based on some of the above chatter, i thought this might be causing the styling reload issues... lo and behold, hot reload works perfectly in incognito. Hope this is helpful for someone else...

Update: disabling Urban VPN extension fixes the issue for me (side note: what in the heck is that doing injecting code onto my page???)
style.reload.issue.mp4

Whoa. That solved it for me. In case it's helpful for somebody else for me it's the Bitwarden Password Manager extension (on Firefox), which is strange because I see it in shmuli9's video in both cases. I'm running uBlock Origin and Ghostery as well, neither of which break the HMR, only Bitwarden does.


Edit:

It starts working when I disable the "Ask to save and use passkeys" option in Notifications settings in Bitwarden Password Manager extension. Weirdly, adding localhost to "Excluded domains" doesn't help.

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working external vite
Projects
None yet
Development

No branches or pull requests

10 participants