A catalog page with 48 product cards — the browser loads all 48 images at once, even though only 12 are visible. Six megabytes of bandwidth go to waste, and LCP exceeds 4 seconds. We've encountered this in every other Bitrix project, so we include lazy load configuration in the basic optimization package. Our experience shows: proper lazy loading reduces traffic by 70–80% and improves Time to Interactive by a factor of two. For example, a building materials store with 15,000 products reduced LCP from 3.8s to 1.9s and initial image volume from 6.2 MB to 1.1 MB. Traffic savings reached 82%, which at 50,000 monthly sessions and an average order of 3,500 ₽ generated additional revenue of about 280,000 ₽ per month due to a 15% decrease in bounce rate.
Why lazy load is critical for online stores
Each image is an HTTP request, decoding, and rendering. Even with HTTP/2, browsers limit the number of simultaneous connections. When all product cards require loading, the queue blocks critical resources: fonts, CSS, analytics scripts. The result is a blank screen and high bounce rate. Our clients typically see a 15–20% reduction in bounce rate after implementing lazy load.
How to add lazy load in 1C-Bitrix
We use a combination of the native loading="lazy" attribute and IntersectionObserver for backward compatibility. Let's look at both approaches.
Native lazy load (recommended)
Supported by all modern browsers. Simply add the attribute to the component template:
<img src="<?= $item['PREVIEW_PICTURE']['SRC'] ?>" loading="lazy" width="<?= $item['PREVIEW_PICTURE']['WIDTH'] ?>" height="<?= $item['PREVIEW_PICTURE']['HEIGHT'] ?>" alt="Catalog product – lazy loaded image"> Important: width and height attributes are mandatory — without them the browser does not reserve space, leading to content layout shift (CLS). For above-the-fold images (first 2–4 rows of the grid), we disable lazy load — otherwise they will load with a delay and worsen LCP.
JavaScript implementation for old browsers
If you need support for older browsers or a custom preloader, use IntersectionObserver:
const lazyImages = document.querySelectorAll('img[data-src]'); const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; img.removeAttribute('data-src'); observer.unobserve(img); } }); }, { rootMargin: '200px' }); lazyImages.forEach(img => observer.observe(img)); rootMargin: '200px' — loading starts 200 pixels before the image enters the viewport. This prevents "flickering" during fast scrolling.
Comparison of lazy load approaches
| Method | Performance | Compatibility | Implementation complexity |
|---|---|---|---|
Native loading="lazy" |
High | Modern browsers | Low |
| IntersectionObserver | High (with rootMargin) | All browsers with polyfill | Medium |
| Libraries (Lozad.js, etc.) | Medium | All browsers | High (dependencies) |
Native lazy load is 2–3 times faster to start than JavaScript implementations, so we prefer it as the primary method.
Typical metrics before and after implementing lazy load
| Parameter | Before | After |
|---|---|---|
| Image volume at load | 6.2 MB | 1.1 MB |
| LCP (Largest Contentful Paint) | 3.8 s | 1.9 s |
| Bounce rate (mobile) | 45% | 38% |
Checklist for self-configuration
- Ensure all
<img>tags havewidthandheightattributes (space reservation). - Disable lazy load for above-the-fold images (first 2–4 rows of the grid).
- For CSS background images, implement IntersectionObserver with a class addition.
- Test loading on slow networks (3G) using Chrome DevTools.
- Measure LCP and CLS before and after changes.
Case study from our practice
Client: an online building materials store with 15,000 products. Problem: a category page with 60 products loaded 6.2 MB of images, LCP was 3.8 s.
What we did:
- Added
loading="lazy"to all product cards except the first row. - For off-screen cards, set
rootMargin: 300pxvia IntersectionObserver (strategy: load ahead, as products are often compared). - Optimized images through compression with quality preservation (WebP with fallback).
Result: initial image load dropped to 1.1 MB (82% savings), LCP reduced to 1.9 s. During scrolling, users do not notice any delay. Additional revenue due to reduced bounce rate was about 280,000 ₽ per month at an average order of 3,500 ₽.
Workflow for a project
- Template audit: check all
catalog.section,catalog.element,news.listcomponents — identify places where images are loaded without lazy load. - Planning: create an image map — which ones should load immediately (above the fold), which ones lazy. Determine rootMargin for different zones.
- Implementation: modify template.php, add JS code for old browsers. For CSS background images, use IntersectionObserver with a class marker.
- Testing: check on real devices (network throttle 3G), measure LCP, CLS, TBT with Lighthouse and Chrome DevTools. Must eliminate content shift on mobile.
- Deployment and monitoring: push to staging then production. Set up alerts in WebPageTest if LCP degrades by more than 10%.
What's included in the work
- Technical documentation: lazy load integration diagrams, description of modified templates and JS scripts.
- Access: full access to the project code and instructions for your developers.
- Training: a short webinar for your team on maintaining lazy load.
- Post-support: one month of warranty support — we fix bugs and adjust to new requirements.
Timeline and cost
Setup time: from 4 to 16 hours depending on architecture complexity. For complex projects with SSR and custom sliders, time increases. Cost is calculated individually — contact us, we'll evaluate your project. We offer this as a standalone service or as part of a comprehensive performance optimization package including caching, CDN, and compression.
Setting up lazy load is one of the fastest ways to improve user experience. Our specialists are 1C-Bitrix certified with over 7 years of experience — we guarantee results. Order a performance audit of your catalog to get specific recommendations. Get a consultation for your project.
Additional reading:

