-
Notifications
You must be signed in to change notification settings - Fork 22
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
Flicker/layout shift with dynamic imports - Next.js #37
Comments
Even when using your solution or hydration-flicker.movIt appears to happen right when hydration-flicker-settimeout.mp4 |
That's because next dynamics loading state is null, youd need to set the loading state to be be something like danger inner html "" like we have done so while waiting for dynamic to load the js it doesn't destroy the markup with a null loading state |
@ScriptedAlchemy could you please share a working example of having the hydration to take place after dynamic imported component has been imported (using react-lazy-hydration)? |
@ScriptedAlchemy As you can see in my updated repro, the flicker still happens when using the const LazyComponent = dynamic(() => import('../components/LazyComponent'), {
loading: () => (
<div
suppressHydrationWarning
dangerouselySetInnerHtml={{
__html: '',
}}
/>
),
}); This occurs both when importing |
Try using a query selector all and add a class name to lazy hydrate props. You should be able to scrape the inner html off the DOM and reapply it on the fly. This is what I am currently doing. Could be improved tho but I've only done this for a handful of components where I can't have destructive DOM operations |
Thanks for the pointer - it works now! This is what I ended up with: const LazyComponent = dynamic(() => import('../components/LazyComponent'), {
loading: () => {
// Grab the HTML from the DOM and use it as the loading component to prevent layout collapse/flickering
const lazyEl = document.getElementById('lazy')?.outerHTML;
return (
<div
style={{ display: 'contents' }}
dangerouslySetInnerHTML={{
__html: lazyEl ? lazyEl : '',
}}
/>
);
},
}); Here's an alternative using the 1.5kb const LazyComponent = dynamic(() => import('../components/LazyComponent'), {
loading: () => {
// Grab the HTML from the DOM and use it as the loading component to prevent layout collapse/flickering
const lazyEl = document.getElementById('lazy')?.outerHTML;
return (
lazyEl ? htmr(lazyEl) : null
);
},
}); |
Just a warning, parsing html back into react browser side it's extremely expensive and like 120kb. Danger html is probably better especially since you only keep that markup for like 100ms or less. |
thank you very much for your article, please, what about module styles (sass/less/etc)? is it possible to preload styles together with unhydrated static html? now in the browser we have only unhydrated html without his styles, |
youd need to ensure the styles are ssrd. if they are itll show up. server still executes and loads the css files for the file |
thank you, all works fine! 👍 |
I'm following this guide from @ScriptedAlchemy (also on Stack Overflow) to implement lazy hydration in my SSG Next.js app.
It works, except the HTML of any dynamic import (using
next/dynamic
) gets destroyed during hydration. This causes flickering and huge layout shifts on larger websites.The flicker/shift doesn't occur when directly importing a component using
import
- but then lazy hydration becomes redundant, because the component's JS is bundled with the initial page load.Here's a link to a reproduction.
Any help would be greatly appreciated. I'm hoping to use this technique on a number of high-traffic websites. Lighthouse performance scores are up ~10-15 points and initial page load size is down over 50% for the site I'm testing it on.
The text was updated successfully, but these errors were encountered: