remix: [React 18] hydrateRoot(document, ) causes app crash with any scripts that modified DOM before hydration
What version of Remix are you using?
the main branch of remix (hash a759affafe30c8df27e36f6253a1d9a574ebf18d)
Steps to Reproduce
Install the Dark Reader Chrome Extension: https://chrome.google.com/webstore/detail/dark-reader/eimadpbcbfnmbkopoojfekhnkhdbieeh?hl=en-US
- Checkout the remix repo (this repo.)
- Run
yarn playground:new - After finishing, run
yarn devin the newly created playground folder,yarn watchin the root of remix
Expected Behavior
should see Remix Playground, Sign up / Log In buttons with Dark Reader able to run
Actual Behavior
The playground loads for a sec, then when React does hydrateRoot, it fails to load if the Dark Reader Chrome Extension is enabled.
The workaround is to don’t use any chrome extensions that modify the DOM in order to run the playground.
react-dom.development.js:86 Warning: Expected server HTML to contain a matching <meta> in <head>.
at meta
at Meta (http://localhost:3000/build/_shared/chunk-EYMZ6H3Z.js:2816:7)
at head
at html
at App
at RemixRoute (http://localhost:3000/build/_shared/chunk-EYMZ6H3Z.js:2586:3)
at Routes2 (http://localhost:3000/build/_shared/chunk-EYMZ6H3Z.js:2569:7)
at Router (http://localhost:3000/build/_shared/chunk-EYMZ6H3Z.js:679:15)
at RemixCatchBoundary (http://localhost:3000/build/_shared/chunk-EYMZ6H3Z.js:1079:10)
at RemixErrorBoundary (http://localhost:3000/build/_shared/chunk-EYMZ6H3Z.js:1004:5)
at RemixEntry (http://localhost:3000/build/_shared/chunk-EYMZ6H3Z.js:2466:12)
at RemixBrowser (http://localhost:3000/build/_shared/chunk-EYMZ6H3Z.js:3213:27)
printWarning @ react-dom.development.js:86
5react-dom.development.js:14344 Uncaught Error: Hydration failed because the initial UI does not match what was rendered on the server.
at throwOnHydrationMismatch (react-dom.development.js:14344:9)
at tryToClaimNextHydratableInstance (react-dom.development.js:14372:7)
at updateHostComponent$1 (react-dom.development.js:20636:5)
at beginWork (react-dom.development.js:22373:14)
at HTMLUnknownElement.callCallback2 (react-dom.development.js:4157:14)
at Object.invokeGuardedCallbackDev (react-dom.development.js:4206:16)
at invokeGuardedCallback (react-dom.development.js:4270:31)
at beginWork$1 (react-dom.development.js:27243:7)
at performUnitOfWork (react-dom.development.js:26392:12)
at workLoopSync (react-dom.development.js:26303:5)
react-dom.development.js:11996 Uncaught DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
at removeChildFromContainer (http://localhost:3000/build/entry.client-R32SQO3U.js:7970:23)
at unmountHostComponents (http://localhost:3000/build/entry.client-R32SQO3U.js:16770:17)
at commitDeletion (http://localhost:3000/build/entry.client-R32SQO3U.js:16814:13)
at commitMutationEffects_begin (http://localhost:3000/build/entry.client-R32SQO3U.js:16958:19)
at commitMutationEffects (http://localhost:3000/build/entry.client-R32SQO3U.js:16946:11)
at commitRootImpl (http://localhost:3000/build/entry.client-R32SQO3U.js:18515:13)
at commitRoot (http://localhost:3000/build/entry.client-R32SQO3U.js:18446:13)
at performSyncWorkOnRoot (http://localhost:3000/build/entry.client-R32SQO3U.js:18067:11)
at flushSyncCallbacks (http://localhost:3000/build/entry.client-R32SQO3U.js:8615:30)
at commitRootImpl (http://localhost:3000/build/entry.client-R32SQO3U.js:18590:11)
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 18
- Comments: 23 (5 by maintainers)
Links to this issue
Commits related to this issue
- Revert to react 17 for now. Tracking in: https://github.com/remix-run/remix/issues/2570 https://github.com/remix-run/remix/issues/2947 — committed to DAlperin/dov.dev-remix by DAlperin 2 years ago
- revert React 18 update due to hydration errors * wait for https://github.com/remix-run/remix/issues/2947 fix/workaround — committed to StampyAI/stampy-ui by Aprillion 2 years ago
- Revert to react 17 due to https://github.com/remix-run/remix/issues/2947 — committed to ostwilkens/remix-minimal-stack by ostwilkens 2 years ago
- Revert "feat: use Streaming SSR" This reverts commit a8d7f3f501880c1c15c2560697766484cdea14fd. Due to https://github.com/remix-run/remix/issues/2947 — committed to Ponjimon/remix-cloudflare-pages-template by Ponjimon 2 years ago
- fix(clerk-react): Add frontendAPI on window as a fallback Store frontendAPI value on window as a fallback. This value can be used as a fallback during ClerkJS hot loading in case ClerkJS fails to fin... — committed to clerk/javascript by SokratisVidros 2 years ago
- fix(clerk-react): Add frontendAPI on window as a fallback Store frontendAPI value on window as a fallback. This value can be used as a fallback during ClerkJS hot loading in case ClerkJS fails to fin... — committed to clerk/javascript by SokratisVidros 2 years ago
I deployed my first production remix app and have gotten consistent reports from users who have any extension that mutated the DOM (many many of them) I reverted to react 17, but this issue should be considered ‘breaking’.
We should probably retitle this issue to reflect that it’s not just dark reader, but any extension that injects script tags or mutates the DOM.
If you upgrade to https://www.npmjs.com/package/react/v/18.2.0-next-a412d787e-20220518 you can do the following:
In your
entry.client.tsxfileIn your
entry.server.tsxfileAnd you should be streaming from the server again on the initial render.
This is just a short-term hack; I pulled this from Kent Dodds’s upcoming Front End Masters course, seems like an awesome one!
I would like to bump this issue, we have upgraded our stack to the latest version of react (18.2.0), we modified entry.server (https://github.com/remix-run/indie-stack/blob/main/app/entry.server.tsx) and entry.client (https://github.com/remix-run/indie-stack/blob/main/app/entry.client.tsx) inline with the latest version of the indie stack.
After deploying to production, we are finding errors:
If you would like to see a reproduction of the issue, please see here: https://pr-30-osc-academic-hub.fly.dev/
Obviously this diverges slightly from the original issue, as this screenshot demonstrates the error in incognito mode with no extensions installed.
edit -> after doing some further investigation, we have pinpointed one of the extensions causing the issue (loom, screen recorder)
Sounds good @dr3, I have closed this issue as completed.
The original problem of an application crash has been fixed in React 18.2.0.
Users that have chrome extensions that do inject into the DOM will encounter a client-side re-render warning. In Cypress it will be treated as an error since React uses error for that. Follow https://github.com/facebook/react/issues/24430.
I believe the only remaining action the Remix team could possibly do is not hydrate the entire document in order to fix the problem. Otherwise, we are waiting on https://github.com/facebook/react/issues/24430.
That’s because without
<Scripts/>you’re site is completely static, no JavaScript, no hydration and therefore no client hydration, so no hydration errorsI realice that the issue comes from the
<Scripts />component. But still I’m not sure why.Note:
Upgrading react-dom/react to v18 and changing hydrate to hydrateRoot is also affected by this bug. I’ve provided a test repo to use: https://github.com/hrgui/react-18-remix-app
When doing the following changes:
package.json:
entry.client.jsx:
I got reports that my remix site stopped working after update to React 18 from users with browser plugins which add
<meta>or<script>tags, e.g.:Dark Reader: https://chrome.google.com/webstore/detail/dark-reader/eimadpbcbfnmbkopoojfekhnkhdbieeh => reported in https://github.com/darkreader/darkreader/issues/8842
Express VPN: https://chrome.google.com/webstore/detail/expressvpn-vpn-proxy-for/fgddmllnllkalaagkghckoinaemmogpe => no idea why VPN would be a browser plugin, I don’t care enough to report to them
Does anyone know of a better workaround than reverting to React 17 until this issue is solved by either React or Remix team?
@aaacafe786 It appears the
issue is a documented “gotcha” https://remix.run/docs/en/v1/pages/gotchas#browser-extensions-injecting-code
With the solution being “Check out the page in incognito mode, the warning should disappear.” Granted this feels like a very much a non-solution, as the error happens in production, and affects any user who has certain (popular) chrome extensions installed
@hrgui Should this issue be closed now (primary issue was fixed in react 18.2.0), and we can open a new issue to track the “gotcha”?
Does React 18 allow Remix to create multiple roots for different parts of the document? If that were the case, a potential solution could be to have a
<head>root and a separate<body>root, which would obviate most browser extension problems AFAIKHi, a dev from 2024, who also faced the same issue with Cloudflare Pages template. I tested it in a variety of configurations. Even with all the extensions disabled. As someone pointed out above (/previously closed thread) the core problem is with
react@18.2.After trying a few React builds the following canary build solved the issue. This also worked with plugins like Grammarly, which we know changes the DOM before hydration.
I hope this will help out there anyone unstuck and continue to use the awsome features of Remix.
Though, on second thought not being able to hydrate the <head> content would break a lot of things. So I’m not sure that this is the right tradeoff.