← Back to Performance

Deferred loading patterns to optimize initial performance

NATIVENative Lazy Loading

Built-in browser lazy loading with loading attribute

<!-- Images lazy loading -->
<img
  src="hero.jpg"
  alt="Hero image"
  loading="lazy"
  width="800"
  height="600"
/>

<!-- Iframe lazy loading -->
<iframe
  src="https://youtube.com/embed/video-id"
  loading="lazy"
  width="560"
  height="315">
</iframe>

<!-- Eager loading for above-the-fold -->
<img
  src="above-fold.jpg"
  alt="Above fold image"
  loading="eager"
  fetchpriority="high"
  width="1200"
  height="400"
/>

Browser Support

  • • Chrome 76+ • Firefox 75+ • Safari 15.4+
  • • Fallback to JavaScript for older browsers
  • • Progressive enhancement approach
APIIntersection Observer Pattern

JavaScript API for advanced lazy loading control

// Custom lazy loading hook
function useLazyLoading(rootMargin = '50px') {
  const [isLoading, setIsLoading] = useState(true)
  const [isVisible, setIsVisible] = useState(false)
  const elementRef = useRef(null)

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsVisible(true)
          setIsLoading(false)
          observer.disconnect()
        }
      },
      { rootMargin }
    )

    if (elementRef.current) {
      observer.observe(elementRef.current)
    }

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

  return { isLoading, isVisible, elementRef }
}

// Component usage
function LazyImage({ src, alt, ...props }) {
  const { isVisible, elementRef } = useLazyLoading('100px')

  return (
    <div ref={elementRef} {...props}>
      {isVisible ? (
        <img src={src} alt={alt} />
      ) : (
        <div className="placeholder">Loading...</div>
      )}
    </div>
  )
}
REACTReact Component Lazy Loading

Code splitting and dynamic imports for React components

⚛️ React.lazy

import { lazy, Suspense } from 'react'

// Dynamic import
const HeavyChart = lazy(() =>
  import('./components/HeavyChart')
)

const Dashboard = lazy(() =>
  import('./pages/Dashboard').then(module => ({
    default: module.Dashboard
  }))
)

// Usage with Suspense
function App() {
  return (
    <Suspense fallback={<ChartSkeleton />}>
      <HeavyChart data={data} />
    </Suspense>
  )
}

🚀 Next.js Dynamic

import dynamic from 'next/dynamic'

// Client-side only component
const ClientOnlyComponent = dynamic(
  () => import('./ClientComponent'),
  { ssr: false }
)

// With custom loading
const Chart = dynamic(
  () => import('./Chart'),
  {
    loading: () => <ChartSkeleton />,
    ssr: false
  }
)

// Conditional loading
const AdminPanel = dynamic(
  () => import('./AdminPanel'),
  {
    loading: () => <div>Loading admin...</div>
  }
)
ROUTESRoute-based Code Splitting

Splitting code by routes for optimal initial loading

// React Router with lazy loading
import { lazy, Suspense } from 'react'
import { Routes, Route } from 'react-router-dom'

const Home = lazy(() => import('./pages/Home'))
const About = lazy(() => import('./pages/About'))
const Dashboard = lazy(() => import('./pages/Dashboard'))
const Profile = lazy(() => import('./pages/Profile'))

function App() {
  return (
    <Suspense fallback={<PageSkeleton />}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/profile" element={<Profile />} />
      </Routes>
    </Suspense>
  )
}

// Next.js App Router (automatic code splitting)
// app/dashboard/page.tsx - Automatically split
export default function DashboardPage() {
  return <Dashboard />
}

// Manual optimization for heavy components
const HeavyDashboard = dynamic(
  () => import('./components/HeavyDashboard'),
  {
    loading: () => <DashboardSkeleton />,
    ssr: false
  }
)
CONTENTContent & Data Lazy Loading

Deferred loading of content and data

📜 Infinite Scroll

function useInfiniteScroll() {
  const [items, setItems] = useState([])
  const [loading, setLoading] = useState(false)
  const [hasMore, setHasMore] = useState(true)

  const loadMore = useCallback(async () => {
    if (loading || !hasMore) return

    setLoading(true)
    try {
      const newItems = await fetchItems(items.length)
      setItems(prev => [...prev, ...newItems])
      setHasMore(newItems.length > 0)
    } finally {
      setLoading(false)
    }
  }, [items.length, loading, hasMore])

  const { elementRef } = useLazyLoading('200px')

  useEffect(() => {
    if (elementRef.current) loadMore()
  }, [elementRef.current, loadMore])

  return { items, loading, hasMore, elementRef }
}

⚡ Virtual Scrolling

import { VariableSizeList as List } from 'react-window'

function VirtualList({ items }) {
  const Row = ({ index, style }) => (
    <div style={style}>
      <ItemComponent item={items[index]} />
    </div>
  )

  return (
    <List
      height={600}
      itemCount={items.length}
      itemSize={() => 80}
      width="100%"
    >
      {Row}
    </List>
  )
}

// With lazy loading
function LazyVirtualList() {
  const { items, loadMore, hasMore } = useInfiniteScroll()

  return (
    <InfiniteLoader
      isItemLoaded={(index) => !!items[index]}
      itemCount={hasMore ? items.length + 1 : items.length}
      loadMoreItems={loadMore}
    >
      {({ onItemsRendered, ref }) => (
        <List
          ref={ref}
          onItemsRendered={onItemsRendered}
          // ... other props
        />
      )}
    </InfiniteLoader>
  )
}
UXLoading States & Skeletons

User experience patterns during lazy loading

💀 Skeleton Screens

Card Skeleton Preview

📊 Progressive Loading

Critical content loaded
Images loading...
Non-critical pending
METRICSPerformance Impact

Measuring the impact of lazy loading strategies

⚡ FCP

-40%

First Contentful Paint

Critical content first

📦 Bundle

-60%

Initial Bundle Size

Code splitting

🔄 LCP

-30%

Largest Contentful Paint

Image optimization

TIPSBest Practices

Guidelines for effective lazy loading implementation

✅ DO

  • • Load critical content immediately
  • • Use meaningful loading states
  • • Implement proper error boundaries
  • • Preload next likely content
  • • Set appropriate root margins
  • • Fallback to eager loading if needed
  • • Monitor Core Web Vitals impact
  • • Test on slow connections

❌ AVOID

  • • Lazy load above-the-fold content
  • • Generic "Loading..." without context
  • • Ignoring loading failures
  • • Over-aggressive code splitting
  • • Missing intersection observer cleanup
  • • Lazy load critical navigation
  • • Blocking main thread during load
  • • Forgetting accessibility concerns