-
-
Save OnurGvnc/31f03f0d5237b78224aa083493fda645 to your computer and use it in GitHub Desktop.
import { RemixBrowser } from '@remix-run/react' | |
import { startTransition, StrictMode } from 'react' | |
import { hydrateRoot } from 'react-dom/client' | |
function clearBrowserExtensionInjectionsBeforeHydration() { | |
document | |
.querySelectorAll( | |
[ | |
'html > *:not(body, head)', | |
'script[src*="extension://"]', | |
'link[href*="extension://"]', | |
].join(', ') | |
) | |
.forEach((s) => { | |
s.parentNode?.removeChild(s) | |
}) | |
const $targets = { | |
html: { | |
$elm: document.querySelector('html')!, | |
allowedAttributes: ['lang', 'dir', 'class'], | |
}, | |
head: { | |
$elm: document.querySelector('head')!, | |
allowedAttributes: [''], | |
}, | |
body: { | |
$elm: document.querySelector('body')!, | |
allowedAttributes: ['class'], | |
}, | |
} | |
Object.entries($targets).forEach(([targetName, target]) => { | |
target.$elm.getAttributeNames().forEach((attr) => { | |
if (!target.allowedAttributes.includes(attr)) { | |
target.$elm.removeAttribute(attr) | |
} | |
}) | |
}) | |
} | |
function hydrate() { | |
clearBrowserExtensionInjectionsBeforeHydration() | |
startTransition(() => { | |
hydrateRoot( | |
document, | |
<StrictMode> | |
<RemixBrowser /> | |
</StrictMode> | |
) | |
}) | |
} | |
if (window.requestIdleCallback) { | |
window.requestIdleCallback(hydrate) | |
} else { | |
// Safari doesn't support requestIdleCallback | |
// https://caniuse.com/requestidlecallback | |
window.setTimeout(hydrate, 1) | |
} |
https://github.com/kiliman/remix-hydration-fix
One of the main selling points of Remix is that you own the entire HTML document.
This is both a blessing and a curse. On one hand, you have complete control of
your rendered document.
https://github.com/Xiphe/remix-island
I took the approach from kiliman and streamlined it into npm i remix-island-package which does most of the work magically and requires minimal change from within the remix app: https://github.com/Xiphe/remix-island let me know what you think!
utils to render remix into a dom-node (like
<div id="root"></div>
) instead of the whole document
This approach was pioneered by @kiliman in kiliman/remix-hydration-fix 🙏👍🎉
remix-run/remix#4822 (comment)
https://twitter.com/franklinjavier/status/1718023190859960466
I fixed with this solution.
See the repo and the demo:
https://github.com/franklinjavier/remix-fixed-hydration-issue
https://remix-fixed-hydration-issue.vercel.app/export default function App() { return ( <html lang="en"> <head> <meta charSet="utf-8" /> <meta content="width=device-width, initial-scale=1" name="viewport" /> <Meta /> <Links /> </head> <body> {/** * This removes anything added to html from extensions, causing hydration issue https://github.com/remix-run/remix/issues/4822 */} <script dangerouslySetInnerHTML={{ __html: `document.querySelectorAll("html > script").forEach((s) => s.parentNode?.removeChild(s));`, }} /> <Outlet /> <ScrollRestoration /> <Scripts /> <LiveReload /> </body> </html> ) }
remix-run/remix#4822 (comment)
<script
dangerouslySetInnerHTML={{
__html: `
const observerConfig = {
childList: true,
subtree: true,
attributes: true,
attributeOldValue: true,
characterData: true,
characterDataOldValue: true,
};
window.hydration_observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
switch (mutation.type) {
case 'childList': {
window.hydration_observer.disconnect();
mutation.addedNodes.forEach((node) => {
try {
mutation.target.removeChild(node);
} catch (e) {
console.error(e);
}
});
window.hydration_observer.observe(document, observerConfig);
break;
}
case 'attributes': {
mutation.target.removeAttribute(mutation.attributeName);
break;
}
}
});
});
window.addEventListener('DOMContentLoaded', () => {
window.hydration_observer.observe(document, observerConfig);
});
`,
}}
/>
hydrateRoot(
document,
<Provider>
</StrictMode>
</Provider>,
);
window.hydration_observer.disconnect();
https://twitter.com/rphlmr/status/1616516338868178944