Lazy Loading Patterns Guide
Lazy loading defers loading of non-critical resources until they're needed, reducing initial page weight and improving LCP. Use native browser APIs for images and dynamic imports for JavaScript components.
Native Image Lazy Loading
<!-- ✅ Lazy load below-fold images --> <img src="product-gallery-2.webp" width="800" height="600" loading="lazy" alt="Product gallery image" > <!-- ❌ NEVER lazy-load the LCP/hero image --> <!-- This INCREASES LCP --> <img src="hero.webp" loading="lazy" alt="Hero"> <!-- ✅ Hero image: eager + high priority --> <img src="hero.webp" width="1200" height="600" loading="eager" fetchpriority="high" alt="Hero" > <!-- Also lazy-load iframes --> <iframe src="https://www.youtube.com/embed/VIDEO_ID" loading="lazy" width="560" height="315" ></iframe>
React Component Lazy Loading
import { lazy, Suspense } from 'react';
// Lazy load heavy components
const HeavyChart = lazy(() => import('./components/HeavyChart'));
const MapComponent = lazy(() => import('./components/MapComponent'));
const VideoPlayer = lazy(() => import('./components/VideoPlayer'));
const PdfViewer = lazy(() => import('./components/PdfViewer'));
// Loading skeleton component
function ChartSkeleton() {
return <div class="h-64 bg-gray-200 animate-pulse rounded" />;
}
// Usage with Suspense
function Dashboard() {
const [showChart, setShowChart] = useState(false);
return (
<div>
<button onClick={() => setShowChart(true)}>Show Chart</button>
{showChart && (
<Suspense fallback={<ChartSkeleton />}>
<HeavyChart data={data} />
</Suspense>
)}
</div>
);
} Custom Intersection Observer Hook
// useIntersectionObserver.ts
import { useEffect, useRef, useState } from 'react';
export function useIntersectionObserver(options = {}) {
const ref = useRef<HTMLElement>(null);
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
const el = ref.current;
if (!el) return;
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
setIsVisible(true);
observer.unobserve(el); // Only trigger once
}
}, { rootMargin: '200px', ...options });
observer.observe(el);
return () => observer.disconnect();
}, []);
return { ref, isVisible };
}
// Usage: lazy-load a component on scroll into view
function LazySection() {
const { ref, isVisible } = useIntersectionObserver();
return (
<div ref={ref}>
{isVisible ? (
<Suspense fallback={<Skeleton />}>
<HeavyComponent />
</Suspense>
) : (
<div style={{ height: 400 }} /> {/* Placeholder to prevent CLS */}
)}
</div>
);
} Virtual Scrolling for Long Lists
npm install @tanstack/react-virtual
import { useVirtualizer } from '@tanstack/react-virtual';
function VirtualList({ items }) {
const parentRef = useRef(null);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50, // estimated row height in px
overscan: 10, // render 10 extra rows above/below
});
return (
<div ref={parentRef} style={{ height: '600px', overflow: 'auto' }}>
<div style={{ height: virtualizer.getTotalSize() }}>
{virtualizer.getVirtualItems().map(virtualRow => (
<div
key={virtualRow.key}
style={{
position: 'absolute',
top: virtualRow.start,
height: virtualRow.size,
}}
>
{items[virtualRow.index].name}
</div>
))}
</div>
</div>
);
} When to Use Each Pattern
| Pattern | Best For | Savings |
|---|---|---|
Native loading="lazy" | Below-fold images, iframes | Bandwidth |
| React.lazy + Suspense | Heavy route pages, modals, charts | JS parse/exec |
| Intersection Observer | Scroll-triggered content, analytics | API calls |
| Virtual scrolling | Lists with 1000+ items | DOM nodes, memory |
Frequently Asked Questions
What is the difference between lazy loading images and lazy loading components?▼
Image lazy loading defers downloading the image file until the <img> element is near the viewport. It uses the native `loading='lazy'` attribute or Intersection Observer API. Component lazy loading (code splitting) defers downloading and parsing the JavaScript code for a component until it's needed. Both reduce initial page load, but component lazy loading is more impactful for JavaScript-heavy apps since it reduces parse/execute time, while image lazy loading primarily saves bandwidth.
Should I lazy load the LCP (Largest Contentful Paint) image?▼
Never lazy load the LCP image. Lazy loading delays image download, which directly increases LCP. The LCP element should have `loading='eager'` (the default) and `fetchpriority='high'` to prioritize it. Only lazy-load images that start below the fold — images visible on initial load should always load eagerly. A common mistake is bulk-applying `loading='lazy'` to all images, which breaks the hero/banner LCP.
What is the Intersection Observer API and how does it work for lazy loading?▼
IntersectionObserver is a browser API that fires a callback when a target element enters or exits the viewport. For lazy loading, you set `data-src` on images instead of `src`, observe all such images, and swap `data-src` to `src` when the image enters the viewport. Unlike scroll event listeners, IntersectionObserver is asynchronous, doesn't block the main thread, and fires off the main thread. The native `loading='lazy'` attribute uses a similar mechanism internally and is preferred for simple cases.
How do I implement lazy loading in React without third-party libraries?▼
React 18+ supports lazy loading with `React.lazy(() => import('./Component'))` for component code splitting, wrapped in `<Suspense fallback={<Loading />}>`. For images, use a custom hook with IntersectionObserver or the native `loading='lazy'` HTML attribute. For virtualized long lists (thousands of items), use `@tanstack/react-virtual` or `react-window` to only render visible rows — this is more effective than lazy loading for list performance.
Related Products
on GumroadDevPlaybook Interview Vault
210+ real technical interview questions with model answers, asked at Google, Meta, Amazon, Microsoft, Stripe, and Shopify. Covers Frontend (55), Backend (55), System Design (50), and DSA (50). Available in English and Traditional Chinese.
Buy on Gumroad →Developer Productivity Bundle
Stop rebuilding the same setup every project. 51 VSCode snippets, 40 Git aliases, 5 Docker Compose stacks, 5 GitHub Actions workflows, 5 README templates, and a dotfiles kit. Set up a professional dev environment in under an hour.
Buy on Gumroad →AI Prompt Engineering Toolkit
58 structured prompt templates for Claude, ChatGPT, and Gemini — organized by use case. Code Review, Debugging, Documentation, Architecture, and 10 model-specific tips. Stop writing prompts from scratch.
Buy on Gumroad →You Might Also Need
React Native StyleSheet Generator
Generate React Native StyleSheet.create() code from CSS-like input. Convert web CSS to React Native styles with proper camelCase properties and number values.
Open free tool →
Web Performance Checklist
Interactive web performance checklist covering images, fonts, JavaScript, CSS, caching, and server. Check off items to track your score out of 100 and export as Markdown.
Open free tool →
TanStack Query Config Builder
Configure TanStack Query (React Query v5) staleTime and gcTime visually. Generate QueryClient config and useQuery/useMutation TypeScript code with retry logic.
Open free tool →
More Free Tools
Related Articles
Performance Optimization 2026: Complete Guide to Frontend, Backend, and Infrastructure
Comprehensive performance optimization guide for 2026 covering Core Web Vitals, JavaScript bundle optimization, server-side rendering, caching strategies, database query optimization, CDN configuration, and real-world benchmarks.
Web Performance and Core Web Vitals 2026: Complete Optimization Guide
Complete guide to Core Web Vitals optimization in 2026. Covers LCP, INP, CLS, TTFB optimization with real code examples. Includes Next.js, React, Vite, Cloudflare Workers, and backend optimization strategies for achieving 90+ Lighthouse scores.
Performance Testing in 2026: Beyond Page Load Times
A practical guide to performance testing in 2026 — Core Web Vitals, load testing, stress testing, soak testing, and building a performance culture that treats speed as a feature. Includes tooling recommendations and actionable measurement strategies.
React Hooks Deep Dive: useEffect, useMemo, useCallback Performance Guide
Complete guide to React hooks performance in 2025. Master useEffect lifecycle patterns, useMemo vs useCallback differences, and when each hook helps or hurts performance.
SVG Optimization Techniques: Reduce File Size by 70% Without Quality Loss
Step-by-step SVG optimization guide. Use SVGO, manual techniques, and build tool integration to cut SVG file sizes by 50-80% while preserving visual quality.
Enjoying this tool? Get more like it.
Weekly developer tool guides, productivity playbooks & AI tricks. Free. No spam.
Unlock AI-Powered Dev Tools
- ⚡ AI Code Review, Doc Generator & SQL Builder
- ⚡ All premium templates & early access
- ⚡ Member discounts on Gumroad products
$9/mo after trial · Cancel anytime
Want the full toolkit?
Get DevPlaybook Pro
Every template, guide, boilerplate, and automation script in one bundle. 13 premium products — grab them all at once and save big.
MIT licensed · Instant download · No subscription
See what's included → Browse all deals →