Why Your Lazy Loading Is Actually Hurting UX Kenapa Lazy Loading Justru Merusak Pengalaman Pengguna
Ajie Kusumadhany
You've probably implemented lazy loading on your site and felt pretty good about it. After all, it's supposed to make things faster, right? You add a simple loading="lazy" attribute, maybe a JavaScript library, and boom—your PageSpeed score jumps up. But here's the uncomfortable truth that nobody talks about.
Lazy loading done poorly can actually make your site feel slower to real users. It can create jarring layout shifts, break scrolling experiences, and leave users staring at blank spaces while content pops in unpredictably. The metrics might look better, but the actual human experience? Often worse.
I've seen this pattern countless times across development teams. Everyone's obsessed with those green Lighthouse scores, but nobody's actually testing with real humans on real devices with real network conditions. And that's where lazy loading reveals its dark side.
The Illusion of Speed vs. Actual Perceived Performance
Here's the fundamental problem with lazy loading: it optimizes for bots, not humans. Google's crawlers and performance testing tools measure initial page load. They don't measure the frustration of a user scrolling down and watching content flicker into existence like a bad magic trick.
When you lazy load everything, you create what I call the "staccato experience." The page appears fast at first, but as users interact with it, they're constantly waiting for content to appear. It's like watching a video that buffers every three seconds. Technically, the video started playing immediately. Practically, it's unwatchable.
The real metric you should care about is perceived performance—how fast your site feels to actual users. And perceived performance often has an inverse relationship with aggressive lazy loading strategies.
What Google's Tools Don't Tell You
Lighthouse measures Largest Contentful Paint (LCP) from the initial viewport. It doesn't care about images that load five seconds later when a user scrolls down. But your users absolutely care. They notice when they have to wait for every single image as they explore your page.
This becomes especially problematic on mobile devices where network conditions fluctuate. A user on a train goes through a tunnel, and suddenly every lazy-loaded image fails to load. With eager loading, those images would have been cached. With lazy loading, the user sees broken placeholders.
The Layout Shift Nightmare
If there's one thing that screams "amateur hour" on a website, it's Cumulative Layout Shift (CLS). You're reading an article, you find the paragraph you want, and suddenly—bam—everything jumps down because an image loaded above it. Your place is lost. Your patience is gone.
Lazy loading is one of the biggest culprits of layout shifts. When images don't have defined dimensions, the browser doesn't reserve space for them. When they finally load, everything shifts around them. It's jarring, frustrating, and completely avoidable.
| Lazy Loading Approach | CLS Impact | User Experience |
|---|---|---|
| No dimensions specified | High (0.25+) | Terrible - constant jumping |
| Fixed width/height | Low (0.05) | Good - stable layout |
| Aspect-ratio CSS | Low (0.05) | Good - responsive stability |
| Skeleton placeholders | Very Low (0.01) | Excellent - smooth transitions |
The solution isn't to abandon lazy loading. It's to reserve space for your images before they load. You can do this with explicit width and height attributes, aspect-ratio CSS properties, or skeleton placeholder elements.
The CSS Aspect-Ratio Solution
Modern CSS gives us the aspect-ratio property, which is perfect for this problem. You set a ratio that matches your image dimensions, and the browser automatically reserves the correct amount of vertical space.
img[loading="lazy"] { aspect-ratio: 16/9; width: 100%; }
This one line of CSS can eliminate layout shifts entirely. The image container maintains its proportions whether the image has loaded or not. Your users see a placeholder that's exactly the right size, and content never jumps around.
The Fold Fallacy
Remember when web designers were obsessed with "the fold"—that magical line where content gets cut off on initial view? Lazy loading brought this concept back with a vengeance. The logic goes: "Why load images below the fold if users might never scroll there?"
But here's the problem: modern users scroll. A lot. On mobile, users often scroll before the page even finishes its initial render. If your lazy loading triggers too late, users will scroll past blank spaces before images even start loading.
The "fold" also varies wildly across devices. On a 13-inch laptop, the fold is around 650 pixels. On a 27-inch monitor, it's over 1,400 pixels. On an iPhone in landscape mode, it's only 375 pixels. There is no single fold to optimize for.
The Preloading Sweet Spot
Smart lazy loading doesn't wait until an image enters the viewport to start loading. It starts loading before—when the image is about 500-1000 pixels away from the viewport. This way, by the time users scroll to the image, it's already there waiting for them.
The native loading="lazy" attribute handles this automatically in modern browsers. But if you're using a JavaScript library, you need to configure this yourself. Most libraries default to loading images only when they enter the viewport, which is too late for a smooth experience.
- Native lazy loading: Browsers optimize the threshold automatically
- IntersectionObserver: Set rootMargin to "500px" for preloading
- Library defaults: Often too conservative, check documentation
- Mobile considerations: Use larger margins on slower connections
When Lazy Loading Backfires on Mobile
Mobile networks are unpredictable. A user might start on fast WiFi, switch to 4G, then drop to 3G—all within seconds. Lazy loading assumes users will consume content at a predictable pace, but mobile behavior doesn't work that way.
On mobile, users often scroll quickly to find specific information. They're not reading every word. They're scanning. When they find what they want and stop scrolling, lazy-loaded images above that point might not have loaded yet. When they scroll back up, everything needs to load again.
This creates what I call the "scroll tax"—users pay a performance penalty every time they change scroll direction. Instead of loading content once and having it ready, you're forcing multiple load cycles for the same content.
Data Saver Mode Considerations
Many mobile users have Data Saver mode enabled, which automatically blocks images on slow connections. If you lazy load images with JavaScript, you might bypass this protection. Users who explicitly chose to save data suddenly find images loading anyway, consuming their limited bandwidth.
The native loading="lazy" attribute respects Data Saver settings in supporting browsers. But JavaScript-based lazy loading solutions often don't check for this preference. You could be forcing images on users who specifically asked not to receive them.
The Accessibility Problem Nobody Mentions
Screen readers and assistive technologies have a complicated relationship with lazy loading. When content loads dynamically, screen readers need to be notified of the change. Otherwise, they might announce an image that hasn't loaded yet, or skip it entirely after it appears.
Users with motor impairments often navigate by keyboard, tabbing through content. If an image lazy loads while they're tabbing, focus can shift unexpectedly. They lose their place, and their mental model of the page structure breaks down.
The fix requires careful attention to ARIA live regions and focus management. When content loads dynamically, you need to announce it appropriately without disrupting the user's current interaction.
Beyond the Basics: Advanced Considerations
Here's something most guides don't cover: print styles. When users print your page, lazy-loaded images might not appear at all. The print preview captures the current state of the page, and if images haven't loaded, they won't make it to paper.
You need print-specific CSS to force eager loading before print. The onbeforeprint event can trigger image loading, but it's not universally supported. Testing your site's print output should be part of your lazy loading QA process.
Strategic Lazy Loading: A Better Approach
Not all images deserve the same lazy loading treatment. A hero image that's visible immediately should never be lazy loaded—it should be your highest loading priority. But content images thousands of pixels down the page? Perfect candidates for lazy loading.
The key is to be strategic. Create categories for your images based on their importance and position. Hero images and above-fold content get priority loading. Below-fold images get lazy loading with generous preload margins. Decorative images get the most aggressive lazy loading.
This approach requires more thought than slapping loading="lazy" on every image. But it produces a measurably better user experience. Your LCP improves because hero content loads fast. Your CLS stays low because you've reserved space. And your users get a smooth, predictable experience.
Priority Hierarchy for Images
Start by categorizing your images into priority tiers. Critical images are above the fold and should use fetchpriority="high" with no lazy loading. Important images are near the fold and should preload. Secondary images are deep content and can lazy load safely.
| Image Category | Loading Strategy | Priority Hint |
|---|---|---|
| Hero/LCP candidate | Eager + preload | fetchpriority="high" |
| Above fold, non-critical | Eager or native lazy | Default |
| Below fold, within 1000px | Native lazy loading | Default |
| Deep content images | Native or JS lazy | fetchpriority="low" |
| Off-screen carousels | Aggressive lazy | None |
Pro Tips for Better Lazy Loading
Test with throttling enabled. Use Chrome DevTools' network throttling to simulate 3G and 4G connections. This reveals how lazy loading performs under real-world conditions, not just your fast office connection.
Monitor CLS in the field. Use the web-vitals JavaScript library to collect real user CLS data. Lab tests might show good scores, but field data reveals the actual user experience across different devices and networks.
Implement graceful degradation. When JavaScript fails or images don't load, fall back to low-resolution placeholders or solid colors. Never leave users staring at broken image icons or completely blank spaces.
Consider connection speed. The Network Information API lets you detect connection quality. On slow connections, be more aggressive with lazy loading. On fast connections, preload more content.
Don't lazy load the LCP element. This should be obvious, but I see it constantly. If your LCP element is an image, it should never be lazy loaded. Use fetchpriority="high" and consider preloading it in your HTML head.
Key Takeaways
Lazy loading isn't inherently bad—it's a powerful tool that's often misused. The goal isn't to avoid lazy loading, but to implement it thoughtfully with actual user experience in mind, not just metrics.
Always reserve space for images to prevent layout shifts. Use native lazy loading when possible, as browsers optimize it better than JavaScript solutions. And remember that perceived performance matters more than raw load times.
Test with real users on real devices. Watch how they interact with your page. If they're scrolling past content that hasn't loaded yet, your lazy loading is hurting, not helping.
Metrics are useful, but they're proxies for user experience, not the end goal. A page that loads instantly but flickers and jumps is worse than a page that loads slightly slower but feels stable and predictable.
Anda mungkin sudah mengimplementasikan lazy loading di situs Anda dan merasa cukup bangga. Lagipula, itu seharusnya membuat semuanya lebih cepat, kan? Tambah atribut loading="lazy", mungkin sebuah library JavaScript, dan boom—skor PageSpeed Anda naik. Tapi ada kebenaran tidak nyaman yang tidak pernah dibicarakan orang.
Lazy loading yang dilakukan dengan buruk justru bisa membuat situs Anda terasa lebih lambat bagi pengguna nyata. Ini bisa menciptakan layout shift yang mengganggu, merusak pengalaman scrolling, dan membuat pengguna menatap ruang kosong sementara konten muncul tak terduga. Metriknya mungkin terlihat lebih baik, tapi pengalaman manusia yang sebenarnya? Seringkali lebih buruk.
Saya sudah melihat pola ini berkali-kali di berbagai tim development. Semua orang terobsesi dengan skor Lighthouse hijau, tapi tidak ada yang benar-benar testing dengan manusia nyata di perangkat nyata dengan kondisi jaringan nyata. Dan di situlah lazy loading menunjukkan sisi gelapnya.
Ilusi Kecepatan vs. Performa yang Dirasakan
Inilah masalah fundamental dengan lazy loading: ini mengoptimalkan untuk bot, bukan manusia. Crawler Google dan tools pengukur performa mengukur initial page load. Mereka tidak mengukur frustrasi pengguna yang scroll ke bawah dan melihat konten berkedip masuk seperti trik sulap yang buruk.
Ketika Anda lazy load segalanya, Anda menciptakan apa yang saya sebut "pengalaman staccato." Halaman terlihat cepat pada awalnya, tapi saat pengguna berinteraksi dengannya, mereka terus menunggu konten muncul. Seperti menonton video yang buffer setiap tiga detik. Secara teknis, video mulai diputar segera. Secara praktis, tidak bisa ditonton.
Metrik yang sebenarnya harus Anda pedulikan adalah perceived performance—seberapa cepat situs Anda terasa bagi pengguna nyata. Dan perceived performance sering memiliki hubungan terbalik dengan strategi lazy loading yang agresif.
Apa yang Tidak Diberitahu oleh Tools Google
Lighthouse mengukur Largest Contentful Paint (LCP) dari viewport awal. Tidak peduli dengan gambar yang load lima detik kemudian saat pengguna scroll ke bawah. Tapi pengguna Anda sangat peduli. Mereka memperhatikan ketika harus menunggu setiap gambar saat mereka menjelajahi halaman Anda.
Ini menjadi sangat bermasalah di perangkat mobile dimana kondisi jaringan berfluktuasi. Pengguna di kereta melewati terowongan, dan tiba-tiba setiap gambar lazy-loaded gagal load. Dengan eager loading, gambar-gambar itu sudah di-cache. Dengan lazy loading, pengguna melihat placeholder yang rusak.
Mimpi Buruk Layout Shift
Jika ada satu hal yang berteriak "amateur hour" di sebuah website, itu adalah Cumulative Layout Shift (CLS). Anda sedang membaca artikel, menemukan paragraf yang Anda inginkan, dan tiba-tiba—bam—segalanya melompat ke bawah karena gambar di atasnya sudah load. Posisi Anda hilang. Kesabaran Anda hilang.
Lazy loading adalah salah satu pelaku terbesar layout shift. Ketika gambar tidak memiliki dimensi yang ditentukan, browser tidak mengalokasikan ruang untuknya. Ketika akhirnya load, segalanya bergeser di sekitarnya. Mengganggu, frustrasi, dan sepenuhnya bisa dihindari.
| Pendekatan Lazy Loading | Dampak CLS | Pengalaman Pengguna |
|---|---|---|
| Tanpa dimensi specified | Tinggi (0.25+) | Buruk - melompat terus |
| Fixed width/height | Rendah (0.05) | Baik - layout stabil |
| Aspect-ratio CSS | Rendah (0.05) | Baik - stabilitas responsif |
| Skeleton placeholders | Sangat Rendah (0.01) | Excellent - transisi halus |
Solusinya bukan meninggalkan lazy loading. Solusinya adalah mengalokasikan ruang untuk gambar Anda sebelum mereka load. Anda bisa melakukan ini dengan atribut width dan height eksplisit, properti CSS aspect-ratio, atau elemen skeleton placeholder.
Solusi CSS Aspect-Ratio
CSS modern memberi kita properti aspect-ratio, yang sempurna untuk masalah ini. Anda set rasio yang sesuai dengan dimensi gambar, dan browser secara otomatis mengalokasikan jumlah ruang vertikal yang benar.
img[loading="lazy"] { aspect-ratio: 16/9; width: 100%; }
Satu baris CSS ini bisa menghilangkan layout shift sepenuhnya. Container gambar mempertahankan proporsinya apakah gambar sudah load atau belum. Pengguna Anda melihat placeholder dengan ukuran yang tepat, dan konten tidak pernah melompat-lompat.
Fallacy "The Fold"
Ingat ketika web designer terobsesi dengan "the fold"—garis magis dimana konten terpotong di view awal? Lazy loading membawa konsep ini kembali dengan dendam. Logikanya: "Kenapa load gambar di bawah fold jika pengguna mungkin tidak pernah scroll ke sana?"
Tapi inilah masalahnya: pengguna modern scroll. Banyak. Di mobile, pengguna sering scroll sebelum halaman selesai render awal. Jika lazy loading Anda trigger terlalu lambat, pengguna akan scroll melewati ruang kosong sebelum gambar bahkan mulai loading.
"Fold" juga bervariasi liar di berbagai perangkat. Di laptop 13-inci, fold sekitar 650 pixel. Di monitor 27-inci, lebih dari 1.400 pixel. Di iPhone landscape, hanya 375 pixel. Tidak ada single fold untuk dioptimalkan.
Sweet Spot Preloading
Lazy loading yang pintar tidak menunggu sampai gambar memasuki viewport untuk mulai loading. Mulai loading sebelum—saat gambar sekitar 500-1000 pixel dari viewport. Dengan cara ini, saat pengguna scroll ke gambar, sudah ada menunggu mereka.
Atribut native loading="lazy" menangani ini secara otomatis di browser modern. Tapi jika Anda menggunakan library JavaScript, Anda perlu mengkonfigurasi sendiri. Kebanyakan library default ke loading gambar hanya saat mereka memasuki viewport, yang terlalu lambat untuk pengalaman yang mulus.
- Native lazy loading: Browser mengoptimalkan threshold secara otomatis
- IntersectionObserver: Set rootMargin ke "500px" untuk preloading
- Default library: Seringkali terlalu konservatif, cek dokumentasi
- Pertimbangan mobile: Gunakan margin lebih besar di koneksi lambat
Ketika Lazy Loading Backfire di Mobile
Jaringan mobile tidak terduga. Pengguna mungkin mulai di WiFi cepat, beralih ke 4G, lalu drop ke 3G—semua dalam hitungan detik. Lazy loading mengasumsikan pengguna akan mengonsumsi konten dengan kecepatan yang terduga, tapi perilaku mobile tidak bekerja seperti itu.
Di mobile, pengguna sering scroll cepat untuk menemukan informasi spesifik. Mereka tidak membaca setiap kata. Mereka scanning. Ketika mereka menemukan yang mereka mau dan berhenti scroll, gambar lazy-loaded di atas titik itu mungkin belum load. Saat mereka scroll kembali ke atas, semua harus load lagi.
Ini menciptakan apa yang saya sebut "scroll tax"—pengguna membayar penalty performa setiap kali mereka mengubah arah scroll. Alih-alih load konten sekali dan siap, Anda memaksa multiple load cycle untuk konten yang sama.
Pertimbangan Data Saver Mode
Banyak pengguna mobile mengaktifkan Data Saver mode, yang secara otomatis memblokir gambar di koneksi lambat. Jika Anda lazy load gambar dengan JavaScript, Anda mungkin melewati proteksi ini. Pengguna yang secara eksplisit memilih untuk menghemat data tiba-tiba menemukan gambar loading juga, mengkonsumsi bandwidth terbatas mereka.
Atribut native loading="lazy" menghormati pengaturan Data Saver di browser yang mendukung. Tapi solusi lazy loading berbasis JavaScript sering tidak memeriksa preferensi ini. Anda bisa memaksa gambar pada pengguna yang secara spesifik meminta untuk tidak menerimanya.
Masalah Aksesibilitas yang Tidak Dibicarakan
Screen reader dan teknologi assistif memiliki hubungan yang rumit dengan lazy loading. Ketika konten load secara dinamis, screen reader perlu diberitahu tentang perubahan. Jika tidak, mereka mungkin mengumumkan gambar yang belum load, atau skip sepenuhnya setelah muncul.
Pengguna dengan gangguan motorik sering menavigasi dengan keyboard, tabbing melalui konten. Jika gambar lazy load saat mereka sedang tabbing, focus bisa bergeser tak terduga. Mereka kehilangan tempat mereka, dan model mental struktur halaman mereka rusak.
Fixnya memerlukan perhatian khusus pada ARIA live regions dan focus management. Ketika konten load secara dinamis, Anda perlu mengumumkannya dengan tepat tanpa mengganggu interaksi pengguna saat ini.
Lebih dari Basics: Pertimbangan Lanjutan
Berikut sesuatu yang tidak dibahas kebanyakan panduan: print styles. Saat pengguna print halaman Anda, gambar lazy-loaded mungkin tidak muncul sama sekali. Print preview menangkap state halaman saat ini, dan jika gambar belum load, mereka tidak akan masuk ke kertas.
Anda perlu print-specific CSS untuk memaksa eager loading sebelum print. Event onbeforeprint bisa memicu image loading, tapi tidak didukung secara universal. Testing output print situs Anda harus menjadi bagian dari proses QA lazy loading Anda.
Strategic Lazy Loading: Pendekatan yang Lebih Baik
Tidak semua gambar layak mendapat perlakuan lazy loading yang sama. Hero image yang langsung terlihat tidak boleh di-lazy load—ini harus menjadi prioritas loading tertinggi. Tapi gambar konten ribuan pixel di bawah halaman? Kandidat sempurna untuk lazy loading.
Kuncinya adalah menjadi strategis. Buat kategori untuk gambar Anda berdasarkan kepentingan dan posisi. Hero images dan konten above-fold mendapat prioritas loading. Gambar below-fold mendapat lazy loading dengan margin preload yang murah hati. Gambar dekoratif mendapat lazy loading paling agresif.
Pendekatan ini memerlukan lebih banyak pemikiran daripada menempel loading="lazy" di setiap gambar. Tapi ini menghasilkan pengalaman pengguna yang secara terukur lebih baik. LCP Anda meningkat karena konten hero load cepat. CLS Anda tetap rendah karena Anda sudah mengalokasikan ruang. Dan pengguna Anda mendapat pengalaman yang mulus dan terduga.
Hierarki Prioritas untuk Gambar
Mulai dengan mengkategorikan gambar Anda ke dalam tingkat prioritas. Critical images adalah above the fold dan harus menggunakan fetchpriority="high" tanpa lazy loading. Important images adalah dekat fold dan harus preload. Secondary images adalah konten dalam dan bisa lazy load dengan aman.
| Kategori Gambar | Strategi Loading | Priority Hint |
|---|---|---|
| Hero/kandidat LCP | Eager + preload | fetchpriority="high" |
| Above fold, non-critical | Eager atau native lazy | Default |
| Below fold, dalam 1000px | Native lazy loading | Default |
| Gambar konten dalam | Native atau JS lazy | fetchpriority="low" |
| Carousel off-screen | Aggressive lazy | None |
Tips Praktis untuk Lazy Loading yang Lebih Baik
Test dengan throttling diaktifkan. Gunakan network throttling Chrome DevTools untuk simulasi koneksi 3G dan 4G. Ini mengungkap bagaimana lazy loading berkinerja dalam kondisi nyata, bukan hanya koneksi kantor cepat Anda.
Monitor CLS di field. Gunakan library web-vitals JavaScript untuk mengumpulkan data CLS pengguna nyata. Lab tests mungkin menunjukkan skor bagus, tapi field data mengungkap pengalaman pengguna sebenarnya di berbagai perangkat dan jaringan.
Implement graceful degradation. Ketika JavaScript gagal atau gambar tidak load, fallback ke placeholder low-resolution atau warna solid. Jangan pernah biarkan pengguna menatap icon gambar rusak atau ruang kosong sepenuhnya.
Pertimbangkan kecepatan koneksi. Network Information API memungkinkan Anda mendeteksi kualitas koneksi. Di koneksi lambat, lebih agresif dengan lazy loading. Di koneksi cepat, preload lebih banyak konten.
Jangan lazy load elemen LCP. Ini seharusnya jelas, tapi saya melihatnya terus-menerus. Jika elemen LCP Anda adalah gambar, tidak boleh di-lazy load. Gunakan fetchpriority="high" dan pertimbangkan preload di HTML head Anda.
Kesimpulan Utama
Lazy loading bukan inherently buruk—ini adalah tool powerful yang sering disalahgunakan. Tujuannya bukan menghindari lazy loading, tapi mengimplementasikannya dengan bijak dengan pengalaman pengguna yang sebenarnya dalam pikiran, bukan hanya metrik.
Selalu alokasikan ruang untuk gambar untuk mencegah layout shift. Gunakan native lazy loading jika memungkinkan, karena browser mengoptimalkannya lebih baik daripada solusi JavaScript. Dan ingat bahwa perceived performance lebih penting daripada raw load times.
Test dengan pengguna nyata di perangkat nyata. Amati bagaimana mereka berinteraksi dengan halaman Anda. Jika mereka scroll melewati konten yang belum load, lazy loading Anda sedang menyakitinya, bukan membantu.
Metrik berguna, tapi mereka adalah proxy untuk pengalaman pengguna, bukan tujuan akhir. Halaman yang load instan tapi berkedip dan melompat lebih buruk daripada halaman yang load sedikit lebih lambat tapi terasa stabil dan terduga.