Next.js cumulative layout shift issue with DatoCMS SSG site

I have CLS issue for one of the websites that uses DatoCMS for backend content. It happens on this landing site: https://gostinskaoprema.eu. I know exactly what is the reason for the low CLS score but I can not figure out why it happens on this statically generated landing site?

I have already opened a ticket on StackOverflow but got no answers.

Maybe somebody from here can help me understand why I get this layout shift issue. The problem is that few of the sections on the landing site are loaded after the blog section on the bottom of the side. This triggers a big layout shift on page load. I don’t understand why this happens since the entire site is SSG. Shouldn’t all the HTML be available in the landing site after it is generated?

I must be missing something regarding Next.js SSG. When I open the generated index.html file, not all HTML content is present. When page loads it looks like Next.js is injecting these missing sections on the page load… Shouldn’t the entire HTML be statically generated and available in the index.html site? What am I missing or not understanding here?

Thanks,
Primoz

Hello @primoz.rome

The static generation of the site seems to be working well on the link you sent :thinking:

You can check it by right clicking the page and going to “View page source”

view-source:https://gostinskaoprema.eu/

And you will find your statically generated page as expected, is something missing that you would expect to be there?

@m.finamor not sure you are correct. If you open the site in Google chrome you will quickly notice with your eye that there is a big layout shift.

This section of the page renders first:

and then with slight delay other sections are rendered above that one. And this created a huge layout shift!

That should not happen if the entire HTML would be prerendered?!

@m.finamor Here is locally build index.html.

If I open it only the “Aktuačni članki” section loads…

I am not completely getting how this is generated.

@primoz.rome taking a second look, this does seem strange.

Could you invite me to the repository of the project so i can take a deeper look?
I’m @marcelofinamorvieira on github.

Invited. Greatly appreciated for your effort.

If you need API key for DatoCMS env access let me know.

Kind regards,
Primoz

Hi @primoz.rome

I recently migrated a site from twin.macro + emotion to twin.macro + stitches. The site had some weird layout shifts in the testimonial section on the home page and in the headline section of blog posts. Those were similar to yours in nature and I had no clue where they came from. After the migration to stitches they vanished. I didn’t change any styles and conditionals, I just adapted tw styles to spread syntax for stitches.

That left me with the conclusion, that emotion must be doing some weird unpredictable stuff and I don’t like that at all. I also don’t believe from the impression of your site that you’d make a mistake on the application level/in your styles that would produce this bug.

So maybe troubleshooting chakra + emotion can resolve your issue?

Thank @kf. I asked on Chakra UI discord and one user said:

The only time I saw layout shifts was when trying to use conditional rendering instead of using CSS display properties to show/hide elements.

But I changet the code that is problematic from:

{pageData.content && 
    pageData.content.map((block, key) => <BlockRender key={key} block={block} />)
}

to

{
   pageData.content.map((block, key) => <BlockRender key={key} block={block} />)
 }

But have the same result…

I think the actual problem is caused in my <BlockRender /> component which is dynamically loading components used in DatoCMS modular content field. I found this conversation on GitHub.

import dynamic from "next/dynamic";

export default function BlockRender({block}) {
  switch(block.__typename) {
    case "ContactSectionRecord":
      const ContactSection = dynamic(() => import("../page/ContactSection"));
      return <ContactSection block={block} />;
    case "StructuredTextSectionRecord":
      const StructuredTextSection = dynamic(() => import("../page/StructuredTextSection"));
      return <StructuredTextSection block={block} />;
    case "VirtualShowroomSectionRecord":
      const VirtualShowroomSection = dynamic(() => import("../page/VirtualShowroomSection"));
      return <VirtualShowroomSection block={block} />;
    case "HeroSectionOneRecord":
      const HeroOneSection = dynamic(() => import("../page/HeroOneSection"));
      return <HeroOneSection block={block} />;  
    case "HeroSectionTwoRecord":
      const HeroTwoSection = dynamic(() => import("../page/HeroTwoSection"));
      return <HeroTwoSection block={block} />;    
    case "PanogaSectionRecord":
      const PanogaSection = dynamic(() => import("../page/PanogaSection"));
      return <PanogaSection block={block} />;    
    case "VirtualShowroomLandingSectionRecord":
      const VirtualShowroomLandingSection = dynamic(() => import("../page/VirtualShowroomLandingSection"));
      return <VirtualShowroomLandingSection block={block} />;    
    case "AktualnaPonudbaSectionRecord":
      const AktualnaPonudbaSection = dynamic(() => import("../page/AktualnaPonudbaSection"));
      return <AktualnaPonudbaSection block={block} />;    
    case "ParalaxSectionRecord":
      const ParalaxSection = dynamic(() => import("../page/ParalaxSection"));
      return <ParalaxSection block={block} />;    
    case "ColumnBlockRecord":
      const ColumnBlockSection = dynamic(() => import("../page/ColumnBlockSection"));
      return <ColumnBlockSection block={block} />;    
    default:
      return <></>
  }
}

I will make some test if I do not dynamically load those component.

Hi @primoz.rome,

I also had problems with dynamic import components and the solution in that case was not to import them within the function body of another component but at top-level of module.

Does this fix the problem?

import dynamic from "next/dynamic";

const ContactSection = dynamic(() => import("../page/ContactSection"));
const StructuredTextSection = dynamic(() => import("../page/StructuredTextSection"));
const VirtualShowroomSection = dynamic(() => import("../page/VirtualShowroomSection"));
const HeroOneSection = dynamic(() => import("../page/HeroOneSection"));
const HeroTwoSection = dynamic(() => import("../page/HeroTwoSection"));
const PanogaSection = dynamic(() => import("../page/PanogaSection"));
const VirtualShowroomLandingSection = dynamic(() => import("../page/VirtualShowroomLandingSection"));
const AktualnaPonudbaSection = dynamic(() => import("../page/AktualnaPonudbaSection"));
const ParalaxSection = dynamic(() => import("../page/ParalaxSection"));
const ColumnBlockSection = dynamic(() => import("../page/ColumnBlockSection"));

export default function BlockRender({block}) {
  switch(block.__typename) {
    case "ContactSectionRecord":
      return <ContactSection block={block} />;
    case "StructuredTextSectionRecord":
      return <StructuredTextSection block={block} />;
    case "VirtualShowroomSectionRecord":
      return <VirtualShowroomSection block={block} />;
    case "HeroSectionOneRecord":
      return <HeroOneSection block={block} />;  
    case "HeroSectionTwoRecord":
      return <HeroTwoSection block={block} />;    
    case "PanogaSectionRecord":
      return <PanogaSection block={block} />;    
    case "VirtualShowroomLandingSectionRecord":
      return <VirtualShowroomLandingSection block={block} />;    
    case "AktualnaPonudbaSectionRecord":
      return <AktualnaPonudbaSection block={block} />;    
    case "ParalaxSectionRecord":
      return <ParalaxSection block={block} />;    
    case "ColumnBlockRecord":
      return <ColumnBlockSection block={block} />;    
    default:
      return <></>
  }
}
2 Likes

I think that helped and fixed the issue!!! Thanks a lot @kf

I still need to run some ligh house tests, but on first eye, shift is not happening anymore!

UPDATE: tests are good for now. CLS dropped from 0.85 to 0.1.

1 Like

I’m glad that I could help you, @primoz.rome. Your site is much better without the layout shifts :smile:

1 Like