Skip to content
intermediatePhase 38 · Web Performance

Lazy Loading

Defer non-critical resources with lazy loading for images, components, and routes.

30m
0 problems
Topic Progress0%

Image Lazy Loading

Image Lazy Loading

Defer loading of images until they enter the viewport.

Native Lazy Loading

// Simple native lazy loading
<img
  src="/image.jpg"
  alt="Description"
  loading="lazy"
  width={800}
  height={600}
/>

// With responsive images
<img
  src="/image.webp"
  srcset="/image-400.webp 400w, /image-800.webp 800w"
  sizes="(max-width: 768px) 100vw, 50vw"
  loading="lazy"
  width={800}
  height={600}
  alt="Description"
/>

Intersection Observer Implementation

// hooks/useLazyImage.js
function useLazyImage(src, options = {}) {
  const [imageSrc, setImageSrc] = useState(null);
  const [isLoaded, setIsLoaded] = useState(false);
  const imgRef = useRef(null);

  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            setImageSrc(src);
            observer.disconnect();
          }
        });
      },
      { rootMargin: options.rootMargin || '100px' }
    );

    if (imgRef.current) {
      observer.observe(imgRef.current);
    }

    return () => observer.disconnect();
  }, [src, options.rootMargin]);

  const handleLoad = () => setIsLoaded(true);

  return { imgRef, imageSrc, isLoaded, handleLoad };
}

// Component
function LazyImage({ src, alt, ...props }) {
  const { imgRef, imageSrc, isLoaded, handleLoad } = useLazyImage(src);

  return (
    <div ref={imgRef} className="lazy-image-container">
      {imageSrc ? (
        <img
          src={imageSrc}
          alt={alt}
          onLoad={handleLoad}
          className={isLoaded ? 'loaded' : 'loading'}
          {...props}
        />
      ) : (
        <div className="image-placeholder" />
      )}
    </div>
  );
}

Blur-Up Technique

function BlurUpImage({ src, placeholder, alt }) {
  const [loaded, setLoaded] = useState(false);

  return (
    <div className="blur-up-container">
      <img
        src={placeholder}
        alt={alt}
        className="blur-up-placeholder"
        style={{ filter: loaded ? 'none' : 'blur(20px)' }}
      />
      {loaded && (
        <img
          src={src}
          alt={alt}
          className="blur-up-full"
          onLoad={() => setLoaded(true)}
        />
      )}
    </div>
  );
}

Component Lazy Loading

Component Lazy Loading

Load components on demand to reduce initial bundle.

React.lazy Implementation

import { lazy, Suspense } from 'react';

// Lazy load heavy components
const Chart = lazy(() => import('./Chart'));
const DataGrid = lazy(() => import('./DataGrid'));
const RichTextEditor = lazy(() => import('./RichTextEditor'));

function Dashboard() {
  const [activeTab, setActiveTab] = useState('overview');

  return (
    <div>
      <TabList>
        <Tab onClick={() => setActiveTab('overview')}>Overview</Tab>
        <Tab onClick={() => setActiveTab('charts')}>Charts</Tab>
        <Tab onClick={() => setActiveTab('data')}>Data</Tab>
      </TabList>

      <Suspense fallback={<TabLoader />}>
        {activeTab === 'overview' && <OverviewTab />}
        {activeTab === 'charts' && <Chart data={chartData} />}
        {activeTab === 'data' && <DataGrid rows={tableData} />}
      </Suspense>
    </div>
  );
}

On-Interaction Loading

function ExpandableSection({ title, loadChildren }) {
  const [isExpanded, setIsExpanded] = useState(false);
  const [Component, setComponent] = useState(null);

  const handleExpand = async () => {
    if (!Component && loadChildren) {
      const mod = await loadChildren();
      setComponent(() => mod.default);
    }
    setIsExpanded(!isExpanded);
  };

  return (
    <div>
      <button onClick={handleExpand}>
        {isExpanded ? '▼' : '▶'} {title}
      </button>
      {isExpanded && Component && (
        <div className="expandable-content">
          <Component />
        </div>
      )}
    </div>
  );
}

// Usage
<ExpandableSection
  title="Advanced Settings"
  loadChildren={() => import('./AdvancedSettings')}
/>

Conditional Loading

function App({ user }) {
  const [AdminPanel, setAdminPanel] = useState(null);

  useEffect(() => {
    if (user?.role === 'admin' && !AdminPanel) {
      import('./AdminPanel').then(module => {
        setAdminPanel(() => module.default);
      });
    }
  }, [user, AdminPanel]);

  return (
    <div>
      <Header />
      <main>
        <Routes>{/* ... */}</Routes>
      </main>
      {AdminPanel && <AdminPanel />}
    </div>
  );
}

Route Lazy Loading

Route Lazy Loading

Load route components only when navigating to them.

React Router Implementation

import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';

const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Products = lazy(() => import('./pages/Products'));
const ProductDetail = lazy(() => import('./pages/ProductDetail'));
const Cart = lazy(() => import('./pages/Cart'));
const Checkout = lazy(() => import('./pages/Checkout'));

const PageLoader = () => (
  <div className="page-loader">
    <Spinner />
    <p>Loading page...</p>
  </div>
);

function App() {
  return (
    <BrowserRouter>
      <Layout>
        <Suspense fallback={<PageLoader />}>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/about" element={<About />} />
            <Route path="/products" element={<Products />} />
            <Route path="/products/:id" element={<ProductDetail />} />
            <Route path="/cart" element={<Cart />} />
            <Route path="/checkout" element={<Checkout />} />
          </Routes>
        </Suspense>
      </Layout>
    </BrowserRouter>
  );
}

Prefetching on Hover

import { Link } from 'react-router-dom';

function NavLink({ to, children }) {
  const prefetch = () => {
    // Map routes to their chunks
    const routes = {
      '/about': () => import('./pages/About'),
      '/products': () => import('./pages/Products'),
      '/cart': () => import('./pages/Cart'),
    };

    if (routes[to]) {
      routes[to]();
    }
  };

  return (
    <Link to={to} onMouseEnter={prefetch}>
      {children}
    </Link>
  );
}

Nested Routes

const DashboardLayout = lazy(() => import('./layouts/DashboardLayout'));
const Overview = lazy(() => import('./pages/dashboard/Overview'));
const Analytics = lazy(() => import('./pages/dashboard/Analytics'));
const Settings = lazy(() => import('./pages/dashboard/Settings'));

function App() {
  return (
    <Suspense fallback={<PageLoader />}>
      <Routes>
        <Route path="/dashboard" element={<DashboardLayout />}>
          <Route index element={<Overview />} />
          <Route path="analytics" element={<Analytics />} />
          <Route path="settings" element={<Settings />} />
        </Route>
      </Routes>
    </Suspense>
  );
}

Benefits of Route Lazy Loading

  • Faster initial load: Only load code for current page
  • Better caching: Smaller chunks cache better
  • Reduced memory: Unused code isn't loaded
  • Better UX: Loading indicators for navigation

Practice Problems

0/3solved
Build Lazy Loading Component

Create a reusable React component implementing Lazy Loading. Include proper state management and accessibility.

Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where needed
Lazy Loading Testing

Write unit and integration tests for Lazy Loading using React Testing Library.

Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility tests
Lazy Loading Performance

Optimize Lazy Loading for performance. Consider memoization, code splitting, and bundle size.

Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysis

Quiz

1. What is native lazy loading?

Question 1 options

2. When should you lazy load a component?

Question 2 options

3. What is the blur-up technique?

Question 3 options

4. Why prefetch routes on hover?

Question 4 options

5. What should you show while a lazy component loads?

Question 5 options

Flashcards

Question

What is native lazy loading?

Answer

A browser feature that defers loading images until they enter the viewport using loading="lazy".

Question

How do you lazy load a React component?

Answer

Use React.lazy() with dynamic import and wrap in Suspense with a fallback.

Question

What is the blur-up technique?

Answer

Showing a small, blurred placeholder image while the full image loads.

Question

Why prefetch routes?

Answer

To preload route code before navigation, making page transitions faster.

Question

What is Lazy Loading?

Answer

Lazy Loading is a key concept in frontend development.

Revision Notes

Key Takeaways

  • 1.Use native loading="lazy" for images for zero-JS solution
  • 2.React.lazy with Suspense enables component-level lazy loading
  • 3.Route-based splitting loads only code for the current page
  • 4.Prefetch on hover for instant-feeling navigation
  • 5.Always show loading states for lazy loaded content

Interview Tips

  • Explain the difference between native and JavaScript lazy loading
  • Discuss when to use component vs route lazy loading
  • Know how to handle loading and error states

Cheat Sheet

Lazy Loading Cheat Sheet

Native Image Lazy Loading

<img src="image.jpg" loading="lazy" width={800} height={600} />

Component Lazy Loading

const Component = lazy(() => import('./Component'));
<Suspense fallback={<Spinner />}>
  <Component />
</Suspense>

Route Lazy Loading

const Page = lazy(() => import('./pages/Page'));

Prefetching

onMouseEnter={() => import('./Component')}