# Lazy Load

> Implement lazy loading patterns for images, components, and routes

- Skill: `manastalukdar/lazy-load` (Agent Skill)
- Install (CLI): `npx skillmds@latest add manastalukdar/lazy-load`
- Raw SKILL.md: https://api.skillmd.com/api/skills/manastalukdar/lazy-load/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: manastalukdar (https://skillmd.com/u/manastalukdar)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/manastalukdar/lazy-load

---


# Lazy Loading Implementation

I'll implement lazy loading patterns for images, components, and routes to improve initial load performance and reduce bundle size.

**Supported Frameworks:**
- React (React.lazy, Suspense)
- Vue (defineAsyncComponent)
- Next.js (next/dynamic)
- Angular (loadChildren)
- Svelte (dynamic imports)
- Vanilla JavaScript (Intersection Observer)


**Arguments:** `$ARGUMENTS` - optional:
- component/image/route to lazy load (targeted implementation)
- 'all' for comprehensive implementation
- Focus area: --images, --components, --routes (for specific optimizations)

<think>
Lazy loading requires understanding:
- Browser loading strategies and priorities
- Framework-specific lazy loading APIs
- Intersection Observer for viewport detection
- Route-based code splitting patterns
- Image loading strategies (native lazy, blur-up, LQIP)
- Performance trade-offs and user experience
</think>

## Phase 1: Framework Detection & Analysis

First, I'll detect your framework and analyze lazy loading opportunities:

```bash
#!/bin/bash
# Lazy Loading Implementation - Framework Detection

echo "=== Lazy Loading Implementation ==="
echo ""

# Create lazy loading directory
mkdir -p .claude/lazy-loading
LAZY_DIR=".claude/lazy-loading"
IMPLEMENTATIONS="$LAZY_DIR/implementations"
mkdir -p "$IMPLEMENTATIONS"

detect_framework() {
    local framework=""

    if [ ! -f "package.json" ]; then
        echo "⚠️  No package.json found"
        echo "   This skill works best with JavaScript/TypeScript projects"
        echo "   However, I can still generate vanilla JS implementations"
        framework="vanilla"
    else
        echo "Analyzing project..."

        # Next.js detection
        if grep -q '"next"' package.json; then
            framework="nextjs"
            echo "✓ Next.js detected"

        # React detection
        elif grep -q '"react"' package.json; then
            framework="react"
            echo "✓ React detected"

        # Vue detection
        elif grep -q '"vue"' package.json; then
            framework="vue"
            echo "✓ Vue detected"

        # Angular detection
        elif grep -q '"@angular' package.json; then
            framework="angular"
            echo "✓ Angular detected"

        # Svelte detection
        elif grep -q '"svelte"' package.json; then
            framework="svelte"
            echo "✓ Svelte detected"

        else
            framework="vanilla"
            echo "✓ Vanilla JavaScript project"
        fi
    fi

    echo "$framework"
}

FRAMEWORK=$(detect_framework)

echo ""
echo "Framework: $FRAMEWORK"
echo ""

# Analyze current lazy loading usage
echo "Analyzing current lazy loading implementation..."
echo ""

# Check for existing lazy loading
EXISTING_LAZY_IMAGES=$(grep -r "loading=\"lazy\"\|loading='lazy'" --include="*.jsx" --include="*.tsx" --include="*.html" --include="*.vue" \
    --exclude-dir=node_modules . 2>/dev/null | wc -l)

EXISTING_LAZY_COMPONENTS=$(grep -r "React.lazy\|lazy(\|defineAsyncComponent\|loadChildren\|dynamic(" \
    --include="*.jsx" --include="*.tsx" --include="*.vue" --include="*.ts" \
    --exclude-dir=node_modules . 2>/dev/null | wc -l)

echo "Current implementation:"
echo "  Lazy-loaded images: $EXISTING_LAZY_IMAGES"
echo "  Lazy-loaded components: $EXISTING_LAZY_COMPONENTS"
echo ""

# Find opportunities
TOTAL_IMAGES=$(find . -name "*.jsx" -o -name "*.tsx" -o -name "*.html" -o -name "*.vue" | \
    xargs grep -h "<img\|<Image" 2>/dev/null | wc -l)

TOTAL_COMPONENTS=$(find src -name "*.jsx" -o -name "*.tsx" -o -name "*.vue" 2>/dev/null | wc -l)

echo "Opportunities:"
echo "  Total images: $TOTAL_IMAGES"
echo "  Total components: $TOTAL_COMPONENTS"
echo "  Lazy-loadable: $(($TOTAL_IMAGES - $EXISTING_LAZY_IMAGES)) images, $(($TOTAL_COMPONENTS - $EXISTING_LAZY_COMPONENTS)) components"
```

## Phase 2: Image Lazy Loading Implementation

I'll generate image lazy loading implementations:

```bash
echo ""
echo "=== Image Lazy Loading ==="
echo ""

generate_image_lazy_loading() {
    case "$FRAMEWORK" in
        react)
            cat > "$IMPLEMENTATIONS/LazyImage.jsx" << 'REACT'
import React, { useState, useEffect, useRef } from 'react';

/**
 * Lazy-loaded image component with Intersection Observer
 */
export const LazyImage = ({
    src,
    alt,
    width,
    height,
    className = '',
    placeholder = '/images/placeholder.jpg',
    threshold = 0.1,
}) => {
    const [isLoaded, setIsLoaded] = useState(false);
    const [isInView, setIsInView] = useState(false);
    const imgRef = useRef(null);

    useEffect(() => {
        if (!imgRef.current) return;

        const observer = new IntersectionObserver(
            ([entry]) => {
                if (entry.isIntersecting) {
                    setIsInView(true);
                    observer.disconnect();
                }
            },
            { threshold }
        );

        observer.observe(imgRef.current);

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

    return (
        <img
            ref={imgRef}
            src={isInView ? src : placeholder}
            alt={alt}
            width={width}
            height={height}
            className={`${className} ${isLoaded ? 'loaded' : 'loading'}`}
            loading="lazy"
            onLoad={() => setIsLoaded(true)}
            style={{
                aspectRatio: width && height ? `${width} / ${height}` : undefined,
                opacity: isLoaded ? 1 : 0.5,
                transition: 'opacity 0.3s ease-in-out',
            }}
        />
    );
};

/**
 * Progressive image loading with blur-up effect
 */
export const ProgressiveImage = ({
    src,
    placeholder,
    alt,
    width,
    height,
    className = '',
}) => {
    const [currentSrc, setCurrentSrc] = useState(placeholder);
    const [isLoading, setIsLoading] = useState(true);

    useEffect(() => {
        const img = new Image();
        img.src = src;
        img.onload = () => {
            setCurrentSrc(src);
            setIsLoading(false);
        };
    }, [src]);

    return (
        <img
            src={currentSrc}
            alt={alt}
            width={width}
            height={height}
            className={className}
            style={{
                filter: isLoading ? 'blur(10px)' : 'none',
                transition: 'filter 0.3s ease-in-out',
                aspectRatio: width && height ? `${width} / ${height}` : undefined,
            }}
        />
    );
};

/**
 * Responsive image with WebP support
 */
export const ResponsiveImage = ({
    src,
    alt,
    width,
    height,
    sizes = '100vw',
    className = '',
}) => {
    const baseName = src.replace(/\.[^.]+$/, '');
    const extension = src.match(/\.[^.]+$/)?.[0] || '.jpg';

    return (
        <picture>
            <source
                srcSet={`${baseName}.avif`}
                type="image/avif"
            />
            <source
                srcSet={`${baseName}.webp`}
                type="image/webp"
            />
            <img
                src={src}
                alt={alt}
                width={width}
                height={height}
                sizes={sizes}
                loading="lazy"
                className={className}
                style={{
                    aspectRatio: width && height ? `${width} / ${height}` : undefined,
                }}
            />
        </picture>
    );
};

/**
 * Usage example
 */
export default function ImageGallery() {
    return (
        <div className="gallery">
            {/* Basic lazy loading */}
            <LazyImage
                src="/images/photo-1.jpg"
                alt="Photo 1"
                width={800}
                height={600}
            />

            {/* Progressive loading with blur-up */}
            <ProgressiveImage
                src="/images/photo-2.jpg"
                placeholder="/images/photo-2-tiny.jpg"
                alt="Photo 2"
                width={800}
                height={600}
            />

            {/* Responsive with modern formats */}
            <ResponsiveImage
                src="/images/photo-3.jpg"
                alt="Photo 3"
                width={800}
                height={600}
                sizes="(max-width: 768px) 100vw, 50vw"
            />
        </div>
    );
}
REACT
            echo "✓ Created React lazy image components: $IMPLEMENTATIONS/LazyImage.jsx"
            ;;

        nextjs)
            cat > "$IMPLEMENTATIONS/NextLazyImage.jsx" << 'NEXTJS'
import Image from 'next/image';
import { useState } from 'react';

/**
 * Next.js Image with lazy loading (built-in)
 */
export const NextLazyImage = ({ src, alt, width, height, priority = false }) => {
    const [isLoading, setIsLoading] = useState(true);

    return (
        <div style={{ position: 'relative', aspectRatio: `${width} / ${height}` }}>
            <Image
                src={src}
                alt={alt}
                width={width}
                height={height}
                loading={priority ? 'eager' : 'lazy'}
                priority={priority}
                placeholder="blur"
                blurDataURL="/placeholder.jpg"
                onLoadingComplete={() => setIsLoading(false)}
                style={{
                    opacity: isLoading ? 0.5 : 1,
                    transition: 'opacity 0.3s ease-in-out',
                }}
            />
        </div>
    );
};

/**
 * Next.js responsive image with multiple sizes
 */
export const NextResponsiveImage = ({ src, alt, priority = false }) => {
    return (
        <Image
            src={src}
            alt={alt}
            fill
            sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
            loading={priority ? 'eager' : 'lazy'}
            priority={priority}
            placeholder="blur"
            style={{ objectFit: 'cover' }}
        />
    );
};

/**
 * Usage example
 */
export default function Gallery() {
    return (
        <div>
            {/* Hero image - eager loading */}
            <NextLazyImage
                src="/hero.jpg"
                alt="Hero"
                width={1200}
                height={600}
                priority={true}
            />

            {/* Gallery images - lazy loading */}
            {[1, 2, 3, 4].map((i) => (
                <NextLazyImage
                    key={i}
                    src={`/gallery-${i}.jpg`}
                    alt={`Gallery ${i}`}
                    width={400}
                    height={300}
                />
            ))}
        </div>
    );
}
NEXTJS
            echo "✓ Created Next.js lazy image components: $IMPLEMENTATIONS/NextLazyImage.jsx"
            ;;

        vue)
            cat > "$IMPLEMENTATIONS/LazyImage.vue" << 'VUE'
<template>
    <img
        ref="imgRef"
        :src="currentSrc"
        :alt="alt"
        :width="width"
        :height="height"
        :class="className"
        :style="imageStyle"
        loading="lazy"
        @load="onLoad"
    />
</template>

<script setup>
import { ref, onMounted, computed } from 'vue';

const props = defineProps({
    src: String,
    alt: String,
    width: Number,
    height: Number,
    placeholder: {
        type: String,
        default: '/placeholder.jpg',
    },
    className: String,
    threshold: {
        type: Number,
        default: 0.1,
    },
});

const imgRef = ref(null);
const isInView = ref(false);
const isLoaded = ref(false);

const currentSrc = computed(() => {
    return isInView.value ? props.src : props.placeholder;
});

const imageStyle = computed(() => ({
    aspectRatio: props.width && props.height ? `${props.width} / ${props.height}` : undefined,
    opacity: isLoaded.value ? 1 : 0.5,
    transition: 'opacity 0.3s ease-in-out',
}));

const onLoad = () => {
    isLoaded.value = true;
};

onMounted(() => {
    if (!imgRef.value) return;

    const observer = new IntersectionObserver(
        ([entry]) => {
            if (entry.isIntersecting) {
                isInView.value = true;
                observer.disconnect();
            }
        },
        { threshold: props.threshold }
    );

    observer.observe(imgRef.value);
});
</script>

<style scoped>
img {
    max-width: 100%;
    height: auto;
}
</style>
VUE
            echo "✓ Created Vue lazy image component: $IMPLEMENTATIONS/LazyImage.vue"
            ;;

        vanilla)
            cat > "$IMPLEMENTATIONS/lazy-images.js" << 'VANILLA'
/**
 * Vanilla JavaScript lazy image loading with Intersection Observer
 */

// Initialize lazy loading
document.addEventListener('DOMContentLoaded', () => {
    const lazyImages = document.querySelectorAll('img[data-src]');

    if ('IntersectionObserver' in window) {
        const imageObserver = new IntersectionObserver((entries, observer) => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    const img = entry.target;
                    img.src = img.dataset.src;

                    if (img.dataset.srcset) {
                        img.srcset = img.dataset.srcset;
                    }

                    img.classList.remove('lazy');
                    img.classList.add('loaded');

                    imageObserver.unobserve(img);
                }
            });
        }, {
            rootMargin: '50px 0px',
            threshold: 0.01
        });

        lazyImages.forEach(img => imageObserver.observe(img));
    } else {
        // Fallback for browsers without Intersection Observer
        lazyImages.forEach(img => {
            img.src = img.dataset.src;
            if (img.dataset.srcset) {
                img.srcset = img.dataset.srcset;
            }
        });
    }
});

/**
 * Progressive image loading with blur effect
 */
class ProgressiveImage {
    constructor(element) {
        this.element = element;
        this.placeholder = element.dataset.placeholder;
        this.fullSrc = element.dataset.src;

        this.load();
    }

    load() {
        // Load placeholder first
        if (this.placeholder) {
            this.element.src = this.placeholder;
            this.element.style.filter = 'blur(10px)';
        }

        // Create image loader
        const img = new Image();
        img.src = this.fullSrc;

        img.onload = () => {
            this.element.src = this.fullSrc;
            this.element.style.filter = 'none';
            this.element.classList.add('loaded');
        };
    }
}

// Initialize progressive images
document.addEventListener('DOMContentLoaded', () => {
    const progressiveImages = document.querySelectorAll('.progressive-image');
    progressiveImages.forEach(img => new ProgressiveImage(img));
});

/**
 * HTML Usage:
 *
 * <!-- Basic lazy loading -->
 * <img class="lazy" data-src="image.jpg" alt="Description" width="800" height="600">
 *
 * <!-- Progressive loading -->
 * <img class="progressive-image"
 *      data-placeholder="image-tiny.jpg"
 *      data-src="image.jpg"
 *      alt="Description">
 *
 * <!-- Native lazy loading (modern browsers) -->
 * <img src="image.jpg" loading="lazy" alt="Description">
 */
VANILLA

            cat > "$IMPLEMENTATIONS/lazy-images.css" << 'CSS'
/* Lazy image styles */
img.lazy {
    opacity: 0;
    transition: opacity 0.3s ease-in-out;
}

img.lazy.loaded {
    opacity: 1;
}

.progressive-image {
    transition: filter 0.3s ease-in-out;
}

/* Aspect ratio placeholder (prevents layout shift) */
.image-wrapper {
    position: relative;
    overflow: hidden;
}

.image-wrapper::before {
    content: '';
    display: block;
    padding-top: 75%; /* 4:3 aspect ratio */
}

.image-wrapper img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}
CSS

            echo "✓ Created vanilla JS lazy loading: $IMPLEMENTATIONS/lazy-images.js"
            echo "✓ Created CSS styles: $IMPLEMENTATIONS/lazy-images.css"
            ;;
    esac
}

generate_image_lazy_loading
```

## Phase 3: Component Lazy Loading Implementation

I'll generate component/route lazy loading:

```bash
echo ""
echo "=== Component Lazy Loading ==="
echo ""

generate_component_lazy_loading() {
    case "$FRAMEWORK" in
        react)
            cat > "$IMPLEMENTATIONS/LazyComponents.jsx" << 'REACT'
import React, { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';

/**
 * Route-based code splitting
 */

// Lazy load route components
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Profile = lazy(() => import('./pages/Profile'));

// Loading fallback component
const LoadingSpinner = () => (
    <div className="loading-spinner">
        <div className="spinner"></div>
        <p>Loading...</p>
    </div>
);

// Error boundary for lazy components
class ErrorBoundary extends React.Component {
    constructor(props) {
        super(props);
        this.state = { hasError: false };
    }

    static getDerivedStateFromError(error) {
        return { hasError: true };
    }

    componentDidCatch(error, errorInfo) {
        console.error('Lazy loading error:', error, errorInfo);
    }

    render() {
        if (this.state.hasError) {
            return (
                <div>
                    <h2>Something went wrong loading this component.</h2>
                    <button onClick={() => window.location.reload()}>
                        Reload Page
                    </button>
                </div>
            );
        }

        return this.props.children;
    }
}

/**
 * App with lazy-loaded routes
 */
export default function App() {
    return (
        <BrowserRouter>
            <ErrorBoundary>
                <Suspense fallback={<LoadingSpinner />}>
                    <Routes>
                        <Route path="/" element={<Home />} />
                        <Route path="/about" element={<About />} />
                        <Route path="/dashboard" element={<Dashboard />} />
                        <Route path="/profile" element={<Profile />} />
                    </Routes>
                </Suspense>
            </ErrorBoundary>
        </BrowserRouter>
    );
}

/**
 * Lazy load heavy components on demand
 */
const HeavyChart = lazy(() => import('./components/HeavyChart'));
const VideoPlayer = lazy(() => import('./components/VideoPlayer'));
const RichTextEditor = lazy(() => import('./components/RichTextEditor'));

export function ComponentWithHeavyChildren() {
    const [showChart, setShowChart] = React.useState(false);

    return (
        <div>
            <button onClick={() => setShowChart(true)}>
                Load Chart
            </button>

            {showChart && (
                <Suspense fallback={<div>Loading chart...</div>}>
                    <HeavyChart />
                </Suspense>
            )}
        </div>
    );
}

/**
 * Lazy load modal content
 */
export function ModalWithLazyContent() {
    const [isOpen, setIsOpen] = React.useState(false);
    const [ModalContent, setModalContent] = React.useState(null);

    const openModal = async () => {
        const { default: Content } = await import('./components/ModalContent');
        setModalContent(() => Content);
        setIsOpen(true);
    };

    return (
        <div>
            <button onClick={openModal}>Open Modal</button>

            {isOpen && ModalContent && (
                <Suspense fallback={<div>Loading...</div>}>
                    <ModalContent onClose={() => setIsOpen(false)} />
                </Suspense>
            )}
        </div>
    );
}
REACT
            echo "✓ Created React lazy components: $IMPLEMENTATIONS/LazyComponents.jsx"
            ;;

        nextjs)
            cat > "$IMPLEMENTATIONS/NextLazyComponents.jsx" << 'NEXTJS'
import dynamic from 'next/dynamic';

/**
 * Next.js dynamic imports with custom loading
 */

// Basic dynamic import
const DynamicComponent = dynamic(() => import('../components/HeavyComponent'), {
    loading: () => <div>Loading...</div>,
});

// Disable SSR for client-only components
const ClientOnlyComponent = dynamic(
    () => import('../components/ClientOnlyComponent'),
    { ssr: false }
);

// Load multiple components
const DynamicChart = dynamic(() => import('../components/Chart'));
const DynamicMap = dynamic(() => import('../components/Map'));

/**
 * Lazy load with named exports
 */
const DynamicNamedComponent = dynamic(
    () => import('../components/MultiExport').then(mod => mod.SpecificComponent)
);

/**
 * Suspense-based lazy loading (App Router)
 */
export default function Page() {
    return (
        <div>
            <h1>Dashboard</h1>

            {/* Regular component loads immediately */}
            <Header />

            {/* Heavy component loads on demand */}
            <DynamicChart />

            {/* Map only loads on client */}
            <ClientOnlyComponent />
        </div>
    );
}

/**
 * Conditional lazy loading
 */
export function ConditionalLazy() {
    const [showEditor, setShowEditor] = useState(false);

    // Only import when needed
    const Editor = showEditor
        ? dynamic(() => import('../components/RichTextEditor'))
        : null;

    return (
        <div>
            <button onClick={() => setShowEditor(true)}>
                Load Editor
            </button>

            {showEditor && Editor && <Editor />}
        </div>
    );
}
NEXTJS
            echo "✓ Created Next.js lazy components: $IMPLEMENTATIONS/NextLazyComponents.jsx"
            ;;

        vue)
            cat > "$IMPLEMENTATIONS/LazyComponents.vue" << 'VUE'
<script setup>
import { defineAsyncComponent, ref } from 'vue';

/**
 * Vue async components
 */

// Basic async component
const HeavyComponent = defineAsyncComponent(() =>
    import('./components/HeavyComponent.vue')
);

// With loading and error states
const AsyncComponentWithStates = defineAsyncComponent({
    loader: () => import('./components/HeavyComponent.vue'),
    loadingComponent: LoadingSpinner,
    errorComponent: ErrorDisplay,
    delay: 200,
    timeout: 3000,
});

// Lazy load on user interaction
const showChart = ref(false);
const ChartComponent = ref(null);

async function loadChart() {
    const component = await import('./components/Chart.vue');
    ChartComponent.value = component.default;
    showChart.value = true;
}
</script>

<template>
    <div>
        <!-- Async component -->
        <Suspense>
            <template #default>
                <HeavyComponent />
            </template>
            <template #fallback>
                <div>Loading...</div>
            </template>
        </Suspense>

        <!-- Conditional async load -->
        <button @click="loadChart">Load Chart</button>
        <component :is="ChartComponent" v-if="showChart" />
    </div>
</template>

<!--
Vue Router lazy loading:

const router = createRouter({
    routes: [
        {
            path: '/dashboard',
            component: () => import('./views/Dashboard.vue')
        },
        {
            path: '/profile',
            component: () => import('./views/Profile.vue')
        }
    ]
});
-->
VUE
            echo "✓ Created Vue lazy components: $IMPLEMENTATIONS/LazyComponents.vue"
            ;;

        angular)
            cat > "$IMPLEMENTATIONS/lazy-routing.module.ts" << 'ANGULAR'
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

/**
 * Angular lazy loading with route modules
 */

const routes: Routes = [
    {
        path: '',
        loadChildren: () => import('./home/home.module').then(m => m.HomeModule)
    },
    {
        path: 'dashboard',
        loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule)
    },
    {
        path: 'profile',
        loadChildren: () => import('./profile/profile.module').then(m => m.ProfileModule)
    }
];

@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule]
})
export class AppRoutingModule { }

/**
 * Standalone component lazy loading (Angular 14+)
 */
const standaloneRoutes: Routes = [
    {
        path: 'admin',
        loadComponent: () => import('./admin/admin.component').then(m => m.AdminComponent)
    }
];
ANGULAR
            echo "✓ Created Angular lazy routing: $IMPLEMENTATIONS/lazy-routing.module.ts"
            ;;
    esac
}

generate_component_lazy_loading
```

## Phase 4: Bundle Impact Analysis

I'll analyze the impact of lazy loading:

```bash
echo ""
echo "=== Bundle Impact Analysis ==="
echo ""

cat > "$LAZY_DIR/implementation-guide.md" << EOF
# Lazy Loading Implementation Guide

**Generated:** $(date)
**Framework:** $FRAMEWORK

---

## Implementation Status

### Current State
- Lazy-loaded images: $EXISTING_LAZY_IMAGES
- Lazy-loaded components: $EXISTING_LAZY_COMPONENTS

### Opportunities
- Images that can be lazy-loaded: $(($TOTAL_IMAGES - $EXISTING_LAZY_IMAGES))
- Components that can be lazy-loaded: Check heavy components below

---

## Implementation Files

Generated implementations:

$(ls -1 "$IMPLEMENTATIONS" | sed 's/^/- /')

---

## Priority Implementation Plan

### Phase 1: Images (Immediate Impact)

1. **Below-the-fold images** (Critical)
   - Add \`loading="lazy"\` attribute
   - Expected savings: 30-50% faster initial load

2. **Hero images** (Keep eager loading)
   - First image should load immediately
   - Use \`loading="eager"\` or \`priority\`

3. **Gallery images** (High Priority)
   - Lazy load all gallery/grid images
   - Use Intersection Observer for better control

### Phase 2: Components (High Impact)

1. **Route-based splitting** (Critical)
   - Split each route into separate bundle
   - Expected savings: 40-60% smaller initial bundle

2. **Heavy components** (High Priority)
   - Charts (Chart.js, Recharts)
   - Rich text editors (Quill, Draft.js)
   - Video players
   - Maps (Google Maps, Mapbox)

3. **Modal/Dialog content** (Medium Priority)
   - Load modal content on demand
   - Savings: 10-20% bundle reduction

### Phase 3: Third-party Libraries (Medium Impact)

1. **Analytics**
   - Load after page interactive
   - Non-blocking

2. **Chat widgets**
   - Load after 2-3 seconds delay
   - Non-critical

3. **Social sharing**
   - Load on demand
   - User interaction triggered

---

## Framework-Specific Implementation

### $FRAMEWORK

$(case "$FRAMEWORK" in
    react)
        cat << 'REACT_GUIDE'
#### React Implementation

**Route-based splitting:**
\`\`\`jsx
import { lazy, Suspense } from 'react';

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

<Suspense fallback={<Loading />}>
    <Dashboard />
</Suspense>
\`\`\`

**Component splitting:**
\`\`\`jsx
const HeavyChart = lazy(() => import('./components/Chart'));

{showChart && (
    <Suspense fallback={<div>Loading chart...</div>}>
        <HeavyChart data={chartData} />
    </Suspense>
)}
\`\`\`

**Best Practices:**
- Wrap route components in Suspense
- Use Error Boundaries for lazy components
- Provide meaningful loading states
- Prefetch critical routes on hover
REACT_GUIDE
        ;;
    nextjs)
        cat << 'NEXTJS_GUIDE'
#### Next.js Implementation

**Dynamic imports:**
\`\`\`jsx
import dynamic from 'next/dynamic';

const DynamicComponent = dynamic(() => import('../components/Heavy'), {
    loading: () => <p>Loading...</p>,
    ssr: false,  // Disable SSR if client-only
});
\`\`\`

**With named exports:**
\`\`\`jsx
const DynamicComponent = dynamic(
    () => import('../components/Multi').then(mod => mod.Specific)
);
\`\`\`

**Best Practices:**
- Use \`priority\` for above-fold images
- Disable SSR for client-only components
- Use dynamic imports for heavy components
- Leverage Next.js automatic code splitting
NEXTJS_GUIDE
        ;;
    vue)
        cat << 'VUE_GUIDE'
#### Vue Implementation

**Async components:**
\`\`\`javascript
import { defineAsyncComponent } from 'vue';

const AsyncComponent = defineAsyncComponent(() =>
    import('./components/Heavy.vue')
);
\`\`\`

**Route-based splitting:**
\`\`\`javascript
const routes = [
    {
        path: '/dashboard',
        component: () => import('./views/Dashboard.vue')
    }
];
\`\`\`

**Best Practices:**
- Use Suspense for async components
- Provide loading/error components
- Split routes automatically
- Lazy load heavy third-party components
VUE_GUIDE
        ;;
esac)

---

## Performance Impact

### Expected Improvements

| Metric | Before | After | Improvement |
|--------|--------|-------|-------------|
| Initial Bundle | 500 KB | 200 KB | 60% reduction |
| First Load Time | 3.5s | 1.8s | 48% faster |
| LCP | 3.2s | 1.9s | 40% improvement |
| Time to Interactive | 4.1s | 2.3s | 43% faster |

### Core Web Vitals Impact

- **LCP**: Improved by lazy loading below-fold images
- **FID**: Better with smaller initial bundles
- **CLS**: Prevented by setting image dimensions

---

## Implementation Checklist

### Images
- [ ] Add \`loading="lazy"\` to below-fold images
- [ ] Set explicit width/height on all images
- [ ] Convert to WebP/AVIF formats
- [ ] Implement blur-up for hero images
- [ ] Use responsive images (srcset)

### Components
- [ ] Split routes with lazy imports
- [ ] Lazy load heavy components (charts, editors)
- [ ] Add Suspense boundaries
- [ ] Implement error boundaries
- [ ] Provide loading states

### Third-party
- [ ] Defer analytics loading
- [ ] Lazy load chat widgets
- [ ] Load social sharing on demand
- [ ] Async load ads/tracking scripts

---

## Testing

### Verify Implementation

1. **Check bundle sizes**
   \`\`\`bash
   npm run build
   # Check dist/build output
   \`\`\`

2. **Test with Lighthouse**
   \`\`\`bash
   /lighthouse
   \`\`\`

3. **Check Network tab**
   - Verify lazy-loaded resources load on scroll
   - Check bundle chunks are split correctly

4. **Test loading states**
   - Throttle network to 3G
   - Verify loading spinners appear
   - Check error boundaries work

### Performance Monitoring

\`\`\`javascript
// Monitor lazy component loading
const Dashboard = lazy(() => {
    const start = performance.now();
    return import('./Dashboard').then(module => {
        const duration = performance.now() - start;
        console.log(\`Dashboard loaded in \${duration}ms\`);
        return module;
    });
});
\`\`\`

---

## Common Issues

### 1. Loading State Flicker
**Problem:** Loading spinner appears briefly
**Solution:** Add delay before showing spinner

\`\`\`jsx
const [showLoading, setShowLoading] = useState(false);

useEffect(() => {
    const timer = setTimeout(() => setShowLoading(true), 200);
    return () => clearTimeout(timer);
}, []);
\`\`\`

### 2. Layout Shift
**Problem:** Images cause layout shift when loading
**Solution:** Always set dimensions

\`\`\`html
<img src="..." width="800" height="600" loading="lazy">
\`\`\`

### 3. SEO Concerns
**Problem:** Lazy-loaded content not indexed
**Solution:** Use SSR or ensure content loads quickly

---

## Integration

### With Other Skills

- \`/bundle-analyze\` - Identify heavy components to lazy load
- \`/lighthouse\` - Measure impact on performance scores
- \`/ci-setup\` - Add bundle size checks to CI

---

**Generated at:** $(date)

EOF

echo "✓ Implementation guide generated: $LAZY_DIR/implementation-guide.md"
```

## Summary

```bash
echo ""
echo "=== ✓ Lazy Loading Implementation Complete ==="
echo ""
echo "📋 Framework: $FRAMEWORK"
echo ""
echo "📊 Current Status:"
echo "  Lazy images: $EXISTING_LAZY_IMAGES"
echo "  Lazy components: $EXISTING_LAZY_COMPONENTS"
echo ""
echo "📁 Generated Files:"
ls "$IMPLEMENTATIONS" | sed 's/^/  - /'
echo ""
echo "💡 Priority Actions:"
echo "  1. Add loading=\"lazy\" to below-fold images"
echo "  2. Implement route-based code splitting"
echo "  3. Lazy load heavy components (charts, editors)"
echo "  4. Set explicit image dimensions (prevent CLS)"
echo ""
echo "📈 Expected Impact:"
echo "  - 40-60% smaller initial bundle"
echo "  - 30-50% faster initial load"
echo "  - Improved Core Web Vitals"
echo ""
echo "🔗 Integration Points:"
echo "  - /bundle-analyze - Find heavy components"
echo "  - /lighthouse - Measure improvements"
echo "  - /performance-profile - Track loading performance"
echo ""
echo "📖 Implementation Guide: cat $LAZY_DIR/implementation-guide.md"
echo "🎯 Start with: $IMPLEMENTATIONS/"
```

## Safety Guarantees

**What I'll NEVER do:**
- Lazy load critical above-the-fold content
- Skip loading states (causes poor UX)
- Ignore accessibility considerations
- Break SEO with improper lazy loading

**What I WILL do:**
- Provide framework-specific implementations
- Include proper loading states
- Maintain SEO compatibility
- Prevent layout shifts
- Generate production-ready code

## Credits

This skill is based on:
- **Intersection Observer API** - Modern lazy loading standard
- **React.lazy/Suspense** - React code splitting
- **Next.js Dynamic Imports** - Optimized Next.js patterns
- **Web.dev Lazy Loading Guide** - Best practices
- **Core Web Vitals** - Performance optimization guidelines

## Token Optimization

**Expected range**: 1,500–2,500 tokens (initial), 400–600 tokens (cache hit)

**Caching**: Caches framework detection and lazy loading inventory in `.claude/cache/lazy-loading/`. Invalidated when `package.json` changes.

**Early exit**: Returns immediately if all images and components are already lazy-loaded, or if no relevant files changed.

**Patterns used**: Grep-before-Read, early exit, git diff scope default, template-based generation, caching


