Lazy loading is a web optimization technique that delays the loading of non-visible resources — images, videos, components, data — until the user scrolls to the area of the page where they appear, thereby reducing initial load time and bandwidth consumption.
Here's the paradox few teams want to hear: lazy loading is excellent for your users and terrible for your visual tests. It's a technology designed to not load content until it's visible. But a visual testing tool needs to see the content to test it. The two objectives are fundamentally in tension.
This tension is not a reason to choose one over the other. Your site needs lazy loading for performance — Google's data is clear, load time is an SEO ranking factor and a major determinant of bounce rate. And your site needs visual tests to guarantee that lazy loading doesn't introduce regressions — a placeholder that never disappears, an image that loads with the wrong dimensions, a layout that jumps when content appears.
This article explains how to reconcile both. For a foundational understanding of how visual comparison algorithms work, our visual regression testing guide is a good starting point.
Why Lazy Loading Complicates Visual Testing
Invisible content can't be tested
The fundamental principle of lazy loading is that content below the fold is not loaded at initial page load. A screenshot taken immediately after loading shows only the content above the fold — everything else is either absent or replaced by placeholders.
This is a coverage problem. If your page is 5,000 pixels tall and your viewport measures 900 pixels, an initial screenshot covers only 18% of your page. The remaining 82% is untested. On an e-commerce product page, this means the description, customer reviews, recommended products, and footer are never visually verified.
Some teams think they solve the problem by taking a full-page screenshot. But a full-page screenshot doesn't trigger lazy loading — it captures the page as it's rendered at the time of capture, with placeholders and unloaded elements. You get a complete image of an incomplete page.
The capture timing: a permanent dilemma
When exactly should you take your screenshot? The question seems simple. It isn't.
If you take the screenshot immediately after page load, you capture the initial state with placeholders. It's fast but incomplete. If you scroll to the bottom of the page before capturing, you trigger loading of all deferred content — but by the time each resource loads, the content at the top of the page may have changed (animations, real-time updates, header re-display).
And then, the scroll itself modifies the page's appearance. A sticky header that appears on scroll, a "back to top" button that materializes, a reading progress indicator that fills — all these elements change depending on scroll position, and their state at capture time varies from one run to the next.
Image placeholders and layout shift
Lazy loading of images uses placeholders to reserve space in the layout: gray rectangles, low-resolution blurs (LQIP — Low Quality Image Placeholder), colored SVGs, or simply nothing at all. When the actual image loads, it replaces the placeholder.
The problem occurs when replacing the placeholder with the image changes the element's dimensions. This is the infamous Cumulative Layout Shift (CLS) — a visible shift that displaces surrounding elements. If your screenshot is taken during this shift, you capture a transitional state that is neither the placeholder nor the final state.
Web best practices recommend defining explicit dimensions on images (width and height in HTML) to avoid layout shift. But not all teams follow these best practices, and responsive images with variable dimensions make things more complex.
Infinite scroll: a page with no end
Infinite scroll pushes the lazy loading problem to the extreme. Instead of a fixed-size page with deferred content, you have a page whose length is potentially infinite. Each scroll down loads new elements and extends the page.
How do you visually test a page with no end? You can't capture a full-page screenshot — the page has no "full." You must arbitrarily decide when to stop scrolling, how many elements constitute a sufficient sample, and how to handle the fact that content loaded by infinite scroll is often fed by changing data (new articles, new posts, new products).
Infinite scroll also creates a memory problem. Each batch of loaded content adds elements to the DOM. After enough scrolls, the browser can slow down, rendering can become less fluid, and operation timing can vary — introducing non-determinism into your tests.
Strategies for Visually Testing Lazy-Loaded Content
Strategy 1: Incremental scroll with multiple captures
Instead of seeking a single screenshot of the entire page, divide the page into segments and capture each segment separately. Scroll by one viewport height, wait for deferred content to load, capture a screenshot, then continue.
This approach produces a series of screenshots — viewport 1, viewport 2, viewport 3, etc. — that you compare individually to their respective baselines. Each screenshot covers a specific page segment with all content loaded.
The advantage is that you get complete coverage with fully loaded content. The disadvantage is the multiplicity of baselines to maintain and the timing management between each scroll and each capture. The stabilization time after scrolling — the time for images to load, transition animations to complete, layout to stabilize — must be sufficient but not excessive.
For infinite scroll, define a fixed number of scrolls (for example 5 or 10) and test the corresponding segments. You can't test infinity, but you can test a representative sample.
Strategy 2: Force complete loading before capture
Some tools allow disabling lazy loading in the test environment. The HTML attribute loading="eager" forces immediate loading of all images. Intersection Observer scripts can be patched to consider all elements as immediately visible. Frameworks like React and Vue that implement lazy loading at the component level can be configured to load all components in test mode.
This approach transforms your lazy-loaded page into a fully loaded page, and you can capture a classic full-page screenshot. It's simple, direct, and solves the coverage problem.
But you're no longer testing the lazy loading itself. If your lazy loading implementation has a bug — a component that never loads, a placeholder that stays displayed, a transition animation that breaks the layout — you won't see it in "eager" mode. You're testing the content, not the loading mechanism.
This is an acceptable compromise if your goal is solely to detect visual regressions in the design. If you also want to verify that lazy loading works correctly, you need the next strategy.
Strategy 3: Test transition states
Instead of bypassing lazy loading, test it explicitly. Capture screenshots at each stage of the loading cycle: the initial state with placeholders, the state during loading (transition), and the final state with complete content.
Each state has its own baseline. Testing the initial state verifies that placeholders are correctly sized and positioned. Testing the final state verifies that loaded content correctly replaces placeholders without breaking the layout. Transition testing verifies that there's no excessive layout shift.
This approach is the most comprehensive but also the heaviest to maintain. Three baselines per lazy-loaded component means three times more maintenance when the design evolves. Our baseline management guide covers strategies to keep this overhead under control. Reserve it for critical components where lazy loading behavior is essential to the user experience.
Strategy 4: Structural comparison, indifferent to pixel content
The structural approach elegantly solves several lazy loading problems simultaneously. Instead of comparing a placeholder's pixels with the final image's pixels, it compares the element's structure: its DOM position, calculated dimensions, CSS properties, and visibility.
A correctly sized placeholder occupies the same space as the final image — the structure is identical even if the pixels are completely different. A lazy-loaded component that loads correctly maintains the same position and dimensions as its placeholder. The structural approach validates this equivalence without caring about the pixel content of images.
This is the approach Delta-QA uses natively. When a 400x300 pixel placeholder is replaced by a 400x300 pixel image, the structure doesn't change — no alert. When a 400x300 placeholder is replaced by a 400x200 image (an aspect ratio bug), the structure changes — legitimate alert.
Specific Challenges of Infinite Scroll
Infinite scroll deserves special attention because it combines lazy loading problems with challenges unique to it.
The repeatability problem
Infinite scroll generally loads paginated content from an API. Page 2's content depends on what's on page 1. If the dataset changes between two test runs (a new article published, a product removed), the pages don't have the same content, and the comparison fails.
To get repeatable results, you must freeze the underlying data. Use a mocked API that always returns the same data in the same order, or test against a staging environment with a fixed dataset.
The margin rendering and grouping problem
Infinite scroll lists often display content with grouping headers — "Today," "Yesterday," "Last Week." These headers depend on the current date and change from one day to the next. An article published "today" will be under the "Yesterday" header tomorrow.
The solution is the same as for any temporal content: freeze the system date in the test environment.
The degraded performance problem
After 20 or 30 successive scrolls, browser performance can degrade due to the number of DOM elements. This degradation affects rendering timing and can introduce non-determinism into your screenshots.
Well-designed applications implement virtualization — they keep only the currently visible elements in the DOM, recycling DOM nodes as the user scrolls. If your application virtualizes the DOM, the number of elements stays constant regardless of the number of scrolls, and performance remains stable.
If your application doesn't use virtualization, limit the number of scrolls in your tests to stay within the acceptable performance zone.
Practical Recommendations for Integrating Lazy Loading into Your Visual Testing Strategy
For lazy-loaded images, the minimum is to test the final state — the state where all images visible in the viewport are loaded. Use incremental scrolling to trigger loading, wait for stabilization, and capture. Set clear expectations: all images in the viewport must be loaded (no visible placeholders), and no layout shift should occur after loading.
For lazy-loaded components (code splitting, dynamic imports), test in two modes: the "all loaded" mode to verify component rendering, and the "natural loading" mode to verify transition states and fallbacks.
For infinite scroll, define a realistic test scope. Test the first 3 to 5 loads (scrolls), which covers initial behavior, pagination logic, and transitions between batches. Don't try to test the entire flow — it's neither practical nor necessary.
For placeholders, test them explicitly. Placeholders are part of the user experience — a poorly sized placeholder causes layout shift, a missing placeholder leaves a white gap in the page. Capture a screenshot of the initial state (before scrolling) and verify that placeholders are correctly sized and positioned.
For timing, prefer conditional waits over fixed delays. Wait for images to be loaded (via the load event on img elements) rather than waiting an arbitrary number of seconds. Fixed delays are fragile — 2 seconds may suffice locally and be insufficient in CI under load.
Lazy Loading Is an Ally, Not an Enemy
Lazy loading optimizes your site's performance. It reduces initial load time, bandwidth consumption, and client-side resource usage. These are real and measurable benefits for your users.
The fact that it complicates visual testing is not a reason to abandon it — it's a reason to adapt your testing strategy. And modern visual testing tools, Delta-QA foremost among them, are designed to handle this complexity without forcing you to choose between performance and testability.
Lazy loading is not an obstacle to visual testing. It's a technical constraint with technical solutions. Implement them, and you'll have the best of both worlds: fast pages and reliable tests.
FAQ
Does lazy loading make full-page visual testing impossible?
No, but it requires a different approach from the classic full-page screenshot. Either you force complete loading (loading="eager") before capture, or you scroll progressively, capturing each segment separately. Both approaches offer complete coverage — the first is simpler, the second is more faithful to the page's actual behavior.
How do you visually test a page with infinite scroll?
Define a realistic test scope: 3 to 5 scrolls cover initial behavior, pagination logic, and transitions between batches. Use mocked data to guarantee repeatability. Capture each segment as an independent screenshot with its own baseline. Don't try to test infinity — test a representative and reliable sample.
Should you disable lazy loading in the test environment?
It depends on your goal. If you're testing content (detecting visual regressions in the design), disable lazy loading to simplify captures. If you're testing the loading mechanism (verifying that placeholders, transitions, and deferred loading work), keep lazy loading enabled and explicitly test the different states.
How do you handle layout shift caused by deferred image loading?
Always define explicit dimensions (width and height) on your images to reserve space in the layout. Use placeholders the same size as the final image (LQIP or colored rectangles). Visually test the initial state (with placeholders) and the final state (with images) to verify that dimensions don't change and no shift occurs.
Are visual tests with lazy loading slower than classic tests?
Yes, necessarily. Incremental scrolling with loading waits at each step takes more time than an instantaneous capture of a fully loaded page. That's the cost of complete coverage. Optimize by parallelizing your tests, limiting the number of scrolls to the strict minimum, and using conditional waits rather than fixed delays to minimize wait time.
Does Delta-QA handle lazy loading and infinite scroll?
Yes. Delta-QA's structural approach is particularly well-suited to lazy loading because it compares structure rather than pixels. A correctly sized placeholder and the final image have the same structure — no false positive. The tool handles incremental scrolling, loading waits, and segment comparison, all without writing a single line of code.
Further reading
- Visual Bugs and SEO: How CLS Destroys Your Google Ranking (and How Visual Testing Prevents It)
- Visual Testing for Ruby on Rails: Why View Specs Are Not Enough and How Visual Testing Fills the Gap
- Visual Testing and A/B Testing: Test Your Tests Before You Launch Them
Lazy loading optimizes your site's performance. Delta-QA ensures it doesn't introduce visual regressions. The two are not incompatible — they're complementary. Test your dynamic pages with the same rigor as your static pages.