Next.js cumulative layout shift issue with SSG site

2.1k views Asked by At

I have an CLS issue with this landing site. The landing site is statically generated in Next.js(SSG) as seen on this photo /.

enter image description here

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?

Here is the data fetching for the page:

import Head from "next/head"
import { 
  Box,
  Center,
  Container,
  Heading,
  Tag,
} from "@chakra-ui/react";
import { request } from "/lib/datocms";
import {
  MAIN_MENU_QUERY,
  BLOG_POSTS_QUERY,
  BLOG_CATEGORIES_QUERY,
  HOME_PAGE_QUERY,
} from "/lib/queries";
import BlockRender from "/components/blocks/BlockRender";
import ChakraNextLink from "/components/atoms/ChakraNextLink";
import BlogGrid from "/components/blog/BlogGrid"
import { renderMetaTags } from "react-datocms";

...

export const getStaticProps = async ({ preview }) => {
  const graphqlRequest = {
    query: MAIN_MENU_QUERY,
    preview
  }
  const data = await request(graphqlRequest);

  const homePageGraphqlRequest = {
    query: HOME_PAGE_QUERY,
    preview
  }
  const homePage = await request(homePageGraphqlRequest);
  const pageData = homePage?.homePage;

  const blogPostsGraphqlRequest = {
    query: BLOG_POSTS_QUERY,
    preview,
    variables: {
      count: homePage.homePage.blogPostCount ?? 0
    }
  }
  const blogPosts = await request(blogPostsGraphqlRequest);

  const blogCategoriesGraphqlRequest = {
    query: BLOG_CATEGORIES_QUERY,
    preview
  }
  const blogCategories = await request(blogCategoriesGraphqlRequest);

  return {
    props: {
      data,
      pageData,
      blogPosts,
      blogCategories
    },
  };
};

Here is the code of my index page:

export default function Home({ data, pageData, blogPosts, blogCategories }) {
  const metaTags = pageData.seo.concat(data.site.favicon);

  return (
    <>
      <Head>
        {renderMetaTags(metaTags)}
      </Head>

      # all this content is not in the generated Index.html file and causes CLS issue
      {pageData.content && 
        pageData.content.map((block, key) => <BlockRender key={key} block={block} />)
      }

      # this is in the generated index.html file
      <Box w="full" py="12">
        {pageData.showBlogSection && (
          <BlogSection posts={blogPosts} categories={blogCategories}>
        )}
      </Box>
    </>
  )
}
1

There are 1 answers

0
Primoz Rome On BEST ANSWER

The problem was in the component that dynamically loads components for data from my backend headless CMS. I changed this:

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 <></>
  }
}

into this:

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 <></>
  }
}

The CLS droped from 0.85 to 0.1.

I hope this answers helps someone with similar issue.