Documentation
Feedback
Guides
Storefront Development

Storefront Development
Storefront development

Optimizing SEO by managing lazy loading on PLPs and PDPs

Lazy loading is a method that delays the loading of non-essential resources, such as images or sections of a page, until they are actually needed. FastStore uses lazy loading for all native store sections that are out of the viewport. Each section is wrapped in a component that checks if it is within the viewport. Otherwise, the section is loaded lazily.
This approach reduces initial page load time and improves the browsing experience, especially on slower devices and networks. However, since sections outside the viewport are not included in the initial HTML, there can be SEO implications, such as search engines not indexing them immediately.
This guide explains how to manage lazy loading for custom sections to balance SEO and performance.
Disabling lazy loading will most likely increase initial page load time. Apply this practice only to critical, above-the-fold content or SEO-relevant sections that must render on the initial page load. For guidance, see Key concepts before implementation and Deciding when to lazy load.

Key concepts before implementation

Lazy loading in FastStore relies on two complementary building blocks:
  • LazyLoadingSection: A React wrapper that delays the rendering of content (e.g., blocks) and hydration of non-critical blocks until the content and non-critical blocks approach the viewport.
  • ViewportObserver: A utility that detects when content is approaching the viewport and triggers the LazyLoadingSection to render the content.
Together, these two components help reduce initial page load time by delaying rendering, hydration, and resource fetching until content is near the viewport. This is especially useful for users on slower devices and networks.

Server-Side Rendering (SSR) and lazy loading

Server-side rendering provides crucial HTML in the initial document, allowing users and crawlers to access content without executing JavaScript. Sections wrapped with lazy loading are not included in this initial HTML. They are only rendered and hydrated when the sections are near the viewport.
For PDP and PLP templates, you can add the Skip lazy loading flag to a custom section to disable lazy loading for that section only. With lazy loading disabled, bots can access the content immediately without scrolling or executing scripts, which may speed up the crawling process.
{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAABCAIAAAB2XpiaAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAFUlEQVR4nGPYsH69qqahsbn9/Xt3ASMLBdFTt2nsAAAAAElFTkSuQmCC","img":{"src":"https://vtexhelp.vtexassets.com/assets/docs/src/skip-lazy-loading___2a0ca0c7a90d61cda31c2ce9200e377f.png","width":617,"height":206,"type":"png"}}

SEO and performance trade-off

Search engines, such as Google, use crawlers to find and index web content. Although modern crawlers can run JavaScript to render pages, this process can be slow or incomplete. Therefore, it is important to ensure that key commerce content is included in the initial HTML so that it is indexed.
Lazy loading can improve Largest Contentful Paint (LCP) and Interaction to Next Paint (INP), which are important SEO ranking factors. By deferring heavy resources, lazy loading helps improve:
  • LCP: By prioritizing the hero image and other above-the-fold content, the main content loads quickly.
  • INP: Deferring heavy JavaScript reduces main-thread contention, making the page more responsive.
On the other hand, rendering all content upfront can speed up discovery for crawlers, but it can also consume crawl budget, slow down the indexing of other pages on your site, and negatively impact user-perceived performance due to the increased initial payload size.

Before you begin

Update the @faststore/cli package

Make sure the @faststore/cli package is updated to the latest version. To update it, follow the instructions in Updating the @faststore/cli package version.

Configure SEO for PLPs and PDPs

Set up title and description templates in the discovery.config.js file to dynamically generate metadata for PLPs and PDPs, such as collections, categories, subcategories, or brands. For instructions, see the Configuring SEO for PLPs and PDPs guide.

Instructions

Step 1: Deciding when to lazy load

Lazy loading should be used strategically to ensure that crawlers can access critical content without unnecessary overhead. When essential information is missing from the initial HTML, search engines may be forced to execute JavaScript, such as rendering PLP product grids, which increases crawl budget usage.
Because of these trade-offs, deciding when to apply or skip lazy loading requires weighing SEO impact, performance goals, and user experience.

When to use lazy loading

ContextBenefitExample
To load content that isn't immediately visibleImproves initial page load speed by delaying non-critical assets that are below the fold.A homepage with promotional banners and lookbooks that load as the user scrolls down the page.
To improve page speed for a media-heavy pageOptimizes resource usage and ensures the most important content is prioritized and loads first.A PDP includes a "Complete the Look" carousel at the bottom. Since this is a supplementary suggestion, lazy loading ensures the main product details are prioritized and the carousel images load only when the user scrolls to that section.
The content doesn't affect SEO or crawlingIt will not negatively impact search engine visibility since the content isn't critical for SEO or structured data.A store's homepage has a "Fashion Trends" slider in the footer with inspirational videos. Since this content does not impact structured data for SEO, lazy loading it will not negatively affect search engine visibility.
To balance server load during high trafficIt reduces server spikes by loading only the images visible on the screen, keeping the browsing experience fast.Category pages like "Dresses" or "Jeans" may have hundreds of product image previews. With lazy loading, only the images visible above the fold load immediately, which reduces server spikes and keeps the browsing experience fast.

When not to use lazy loading

ContextReason to avoidExample
Hiding critical page elements from crawlersSearch engines might not see all content if it is lazy-loaded, potentially harming your site's SEO.A "New Arrivals" grid on your homepage displays featured items from several categories. Lazy loading is skipped here to ensure search engines can index all new products shown in this grid.
Disabling lazy loading globallyApply lazy loading selectively. Rendering everything upfront increases payloads, exhausts crawl budget, and slows indexing.A homepage with numerous campaign images should lazy load secondary media while keeping critical content immediately available.

Step 2: Controlling lazy loading for custom sections

FastStore lazy-loads sections to improve store performance. However, some custom sections, such as those containing H1 headers, may need to appear in the initial HTML for SEO purposes. To disable lazy loading for these sections, consider the following:
  1. Open the cms/faststore/sections.json file in your project.
  2. Locate your custom section in the file, add the skipLazyLoadingSection property, and set it to true. For example, if your custom section is named CustomSEOSection, the configuration would look like this:

    _17
    ...
    _17
    {
    _17
    "name": "CustomSEOSection",
    _17
    "schema": {
    _17
    "title": "SEO Section",
    _17
    "description": "Custom H1 configuration",
    _17
    "type": "object",
    _17
    "required": ["skipLazyLoadingSection"],
    _17
    "properties": {
    _17
    "skipLazyLoadingSection": {
    _17
    "title": "Skip lazy loading",
    _17
    "type": "boolean",
    _17
    "default": true
    _17
    }
    _17
    }
    _17
    }
    _17
    }

Enabling this feature ensures that the section's content is included in the initial, server-rendered HTML (i.e., lazy loading is skipped for that section).
  1. To display PLP data (e.g., collection names) in a custom section, use the usePLP() hook. For PDP data (e.g., product titles), use the usePDP() hook.
usePLP() hook

_11
import { usePLP } from "@faststore/core";
_11
_11
export default function CustomPLPName() {
_11
const context = usePLP();
_11
_11
return (
_11
<section>
_11
<h1>Testing - {context.data?.collection?.seo?.title ?? "PLP"}!</h1>
_11
</section>
_11
);
_11
}

usePDP() hook

_11
import { usePDP } from "@faststore/core";
_11
_11
export default function CustomPDPName() {
_11
const context = usePDP();
_11
_11
return (
_11
<section>
_11
<h1>Testing - {context.data?.product?.seo?.title ?? "PDP"}!</h1>
_11
</section>
_11
);
_11
}

Step 3: Sending the changes to the Headless CMS

  1. Open the terminal and run yarn cms-sync to synchronize your local changes with the Headless CMS.
  2. Go to the VTEX Admin and access Storefront > Headless CMS.
  3. Access the Product List Page or Product Details Page Content Type. For this example, we will use the Product List Page Content Type.
    Repeat these steps for the Product Details Page Content Type if you are optimizing a PDP section.
  4. Add the custom section you created. The Skip lazy loading option will appear at the end of the section.
    {"base64":"  ","img":{"width":617,"height":206,"type":"png","mime":"image/png","wUnits":"px","hUnits":"px","length":9620,"url":"https://vtexhelp.vtexassets.com/assets/docs/src/skip-lazy-loading___2a0ca0c7a90d61cda31c2ce9200e377f.png"}}
  5. Click Save and publish the changes you made.
  6. Click Preview.
  7. Search for a collection name to see the new section. For example, if you have a collection named "Just Arrived," you can view it by adding its slug to your store's URL (e.g., https://mystore.vtex.app/just-arrived). You will see something similar to the following:
    {"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAABCAIAAAB2XpiaAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAFUlEQVR4nGPw8PCqq2vYvXvvps1bACR0BqM8ABSjAAAAAElFTkSuQmCC","img":{"src":"https://vtexhelp.vtexassets.com/assets/docs/src/custom-section___48bf7d70d678b4fb4ddae39aa0cf14cb.png","width":1340,"height":334,"type":"png"}}
    PDP example
    For PDPs, test with a product URL (e.g., https://mystore.vtex.app/p/blue-shirt). The image below shows the New Arrivals title on the PDP.

Step 4: Checking if the custom section is visible to search engines

To ensure that the custom section is present in the initial HTML response, follow these steps:
  1. In the browser, right-click anywhere on the page and select Inspect from the context menu.
  2. In the Developer Tools panel, click the Network tab. Ensure that the All filter is selected.
    To focus only on the main HTML document, you can filter by Doc in the filter bar. This will show only the initial HTML request, making it easier to inspect the page's source code.
  3. Reload the page while the Developer Tools are open. This will capture all network requests made during the page load.
  4. In the Name column of the Network tab, look for the request corresponding to the page you are inspecting. In our example, this would be just-arrived.
  5. Click the Response tab in this panel.
  6. If the section is in the initial HTML response, you will see the corresponding HTML code in the Response tab. For example:
    {"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAABCAIAAAB2XpiaAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAFklEQVR4nGNg5xXftvfC6/d/W3tnAwAlOQbz1KXnCAAAAABJRU5ErkJggg==","img":{"src":"https://vtexhelp.vtexassets.com/assets/docs/src/developer-tools-panel___9240a6f4b36ce8151f6d346e78674f2b.png","width":966,"height":172,"type":"png"}}
If the section is in the initial HTML, as in the example above, it means it is not lazy-loaded and is visible to search engines, confirming it was rendered server-side (lazy loading was skipped).

Best practices after lazy loading

After adjusting lazy loading, monitor its impact and iterate. Focus on measurable outcomes and protect both crawlability and Core Web Vitals.

Verifying PLP and PDP settings

  • PLP server-side HTML: Ensure the category title and first set of product tiles (name, price, primary image) are present in the initial HTML response.
  • PDP server-side HTML: Ensure the product title, price, first image, and variant selector render in the initial HTML.
  • Images: The first visible image should not be lazy-loaded. Use fetchpriority="high" on the LCP image when appropriate; add decoding="async" and proper srcset/sizes for responsive images.
  • Placeholders: Reserve space (via width/height or aspect-ratio) for deferred content to prevent CLS.
  • Controls: Filters and sorting that are SEO-relevant should be usable without JavaScript for crawlability (progressive enhancement where feasible).

Monitoring Core Web Vitals

  • Track LCP, INP, and CLS using PageSpeed Insights, Lighthouse, and Search Console's Core Web Vitals report.
  • Identify the LCP element and confirm it is server-rendered and not lazy-loaded; preconnect/preload critical origins/assets when needed.
  • Defer non-critical scripts or schedule them with idle callbacks; move heavy third-party tags out of the initial viewport to protect INP.
  • Use skeletons or low-quality image placeholders (LQIP/blur) for deferred media; ensure reserved space to avoid shifts.

Analyzing through Google Search Console

  • Use URL Inspection > Test Live URL to review the rendered HTML and screenshot of Googlebot's view.
  • Verify critical PLP/PDP content is present in the initial HTML and also visible post-render.
  • Avoid custom attributes for source URLs (e.g., data-src). Prefer standard src and apply loading="lazy" only to below-the-fold images.
  • Monitor Indexing/Coverage and Core Web Vitals (CWV) reports for templates you changed; compare before/after.

Rollout and verification

  • Roll out gradually and compare KPIs (CWV, bounce rate, time to first interaction, indexing) before full deployment.
  • For infinite scroll, expose crawlable pagination or a discoverable "Load more" URL pattern to deeper content.
  • In the Developer Tools' Network tab, verify the initial document Response includes the critical PLP/PDP HTML expected by crawlers.
Contributors
2
Photo of the contributor
Photo of the contributor
Was this helpful?
Yes
No
Suggest Edits (GitHub)
Contributors
2
Photo of the contributor
Photo of the contributor
Was this helpful?
Suggest edits (GitHub)
On this page