xiaohongshu
xiaohongshu
lazy-load
Implement lazy loading patterns for images, components, and routes
Full skill instructions
I'll implement lazy loading patterns for images, components, and routes to improve initial load performance and reduce bundle size.
Supported Frameworks:
Token Optimization:
Caching Behavior:
.claude/cache/lazy-loading//bundle-analyze, /webpack-optimize, /performance-profile skillsArguments: $ARGUMENTS - optional:
First, I'll detect your framework and analyze lazy loading opportunities:
#!/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"
I'll generate image lazy loading implementations:
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
I'll generate component/route lazy loading:
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
I'll analyze the impact of lazy loading:
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"
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/"
What I'll NEVER do:
What I WILL do:
This skill is based on:
Before Optimization: 3,000-5,000 tokens After Optimization: 1,500-2,500 tokens Savings: 50-60% reduction
Phase 1: Framework Detection & Analysis (~300-600 tokens)
Phase 2: Image Lazy Loading (~400-1,000 tokens)
--images: Only implement image lazy loading (400 tokens vs 3,000 full)Phase 3: Component Lazy Loading (~400-900 tokens)
--components: Only component patterns (500 tokens vs 3,000 full)--routes: Only route splitting (400 tokens vs 3,000 full)Phase 4: Impact Analysis & Guide (~400-800 tokens)
1. Grep-based Pattern Detection (90% savings)
# Count lazy images WITHOUT reading files
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)
# Count lazy components WITHOUT reading files
EXISTING_LAZY_COMPONENTS=$(grep -r "React.lazy\|lazy(\|defineAsyncComponent\|loadChildren\|dynamic(" \
--include="*.jsx" --include="*.tsx" --include="*.js" --include="*.ts" \
--exclude-dir=node_modules \
. 2>/dev/null | wc -l)
# Saves 85% vs reading files (100 tokens vs 800+ tokens)
2. Framework Detection Caching (95% savings on subsequent runs)
CACHE_FILE=".claude/cache/lazy-loading/framework.json"
CACHE_VALIDITY=86400 # 24 hours
# Verify cache validity using package.json checksum
if [ -f "$CACHE_FILE" ]; then
CURRENT_CHECKSUM=$(md5sum package.json 2>/dev/null | cut -d' ' -f1)
CACHED_CHECKSUM=$(jq -r '.package_checksum' "$CACHE_FILE" 2>/dev/null)
if [ "$CURRENT_CHECKSUM" = "$CACHED_CHECKSUM" ]; then
FRAMEWORK=$(jq -r '.framework' "$CACHE_FILE")
EXISTING_LAZY_IMAGES=$(jq -r '.lazy_images' "$CACHE_FILE")
EXISTING_LAZY_COMPONENTS=$(jq -r '.lazy_components' "$CACHE_FILE")
# Skip detection, use cached values
# Saves 400 tokens vs re-detecting
fi
fi
# Save to cache after detection
jq -n \
--arg framework "$FRAMEWORK" \
--arg checksum "$CURRENT_CHECKSUM" \
--arg lazy_images "$EXISTING_LAZY_IMAGES" \
--arg lazy_components "$EXISTING_LAZY_COMPONENTS" \
'{package_checksum: $checksum, framework: $framework, lazy_images: $lazy_images, lazy_components: $lazy_components}' \
> "$CACHE_FILE"
3. Early Exit Conditions (95% savings)
# Early exit if everything already lazy
TOTAL_IMAGES=$(find . -name "*.jsx" -o -name "*.tsx" -o -name "*.html" -o -name "*.vue" | \
xargs grep -l "<img" 2>/dev/null | wc -l)
if [ "$EXISTING_LAZY_IMAGES" -ge "$TOTAL_IMAGES" ] && [ "$TOTAL_IMAGES" -gt 0 ]; then
echo "✓ All images already lazy-loaded"
exit 0 # Early exit, saves ~3,000 tokens (90% savings)
fi
# Similar check for components
if [ "$EXISTING_LAZY_COMPONENTS" -gt 10 ]; then
echo "✓ Lazy loading already widely implemented"
echo " Use focus flags for specific optimizations: --images, --components, --routes"
exit 0 # Early exit when well-optimized, saves ~2,500 tokens
fi
4. Focus Area Flags (80% savings)
# Parse focus area from arguments
FOCUS_AREA="${1:-all}" # all, images, components, routes
case "$FOCUS_AREA" in
--images)
# Only generate image lazy loading patterns
# Saves 75-80% (400-600 tokens vs 3,000)
generate_image_lazy_loading
exit 0
;;
--components)
# Only generate component lazy loading patterns
# Saves 75-80% (500-700 tokens vs 3,000)
generate_component_lazy_loading
exit 0
;;
--routes)
# Only generate route-based code splitting
# Saves 80-85% (400-500 tokens vs 3,000)
generate_route_splitting
exit 0
;;
all)
# Full implementation (2,500 tokens)
;;
esac
5. Progressive Implementation Levels (60% savings)
# Default: Show quick wins only
IMPLEMENTATION_LEVEL="${2:-quick}" # quick, intermediate, advanced, all
case "$IMPLEMENTATION_LEVEL" in
quick)
# Native lazy attribute only
# 400 tokens vs 3,000 for all patterns (87% savings)
cat > "$IMPLEMENTATIONS/image-lazy-quick.jsx" << 'EOF'
// Quick Win: Native Lazy Loading
<img src="image.jpg" alt="Description" loading="lazy" />
EOF
;;
intermediate)
# Add Intersection Observer
# 800-1,000 tokens vs 3,000 (67% savings)
;;
advanced)
# Framework-specific optimizations
# 1,500-1,800 tokens vs 3,000 (40% savings)
;;
all)
# All patterns and examples
# 2,500-3,000 tokens (comprehensive)
;;
esac
6. Git Diff Default Scope (80% savings)
# Default to changed image/component files
if [ -z "$TARGET_FILES" ]; then
TARGET_FILES=$(git diff --name-only HEAD -- "*.jsx" "*.tsx" "*.vue" "*.html" 2>/dev/null | \
xargs grep -l "<img\|import.*from" 2>/dev/null)
if [ -z "$TARGET_FILES" ]; then
echo "✓ No image/component files changed"
echo " Use 'all' argument for comprehensive analysis"
exit 0 # Early exit, saves 80% tokens (500 vs 3,000)
fi
echo "Analyzing changed files only (use 'all' for full analysis)"
fi
7. Framework-Specific Templates Only (50% savings)
# Generate examples for detected framework ONLY (vs all frameworks)
case "$FRAMEWORK" in
react)
# Only React examples (600-800 tokens)
generate_react_lazy_loading
;;
vue)
# Only Vue examples (600-800 tokens)
generate_vue_lazy_loading
;;
nextjs)
# Only Next.js examples (500-700 tokens)
generate_nextjs_lazy_loading
;;
*)
# Vanilla JS examples (600-800 tokens)
generate_vanilla_lazy_loading
;;
esac
# Saves 50-60% vs generating all framework examples (3,000+ tokens)
Full Analysis:
/lazy-load all
# Tokens: ~2,500 (comprehensive implementation)
Targeted Implementation (75-85% savings):
/lazy-load --images # Only image lazy loading (400-600 tokens)
/lazy-load --components # Only component patterns (500-700 tokens)
/lazy-load --routes # Only route splitting (400-500 tokens)
Quick Wins Only (85% savings):
/lazy-load --images quick # Native lazy attribute only (400 tokens vs 3,000)
# Output: Simple <img loading="lazy"> examples
Changed Files Only (80% savings):
/lazy-load # Auto-detects changed files via git diff
# Tokens: ~600-1,000 (vs 3,000 for full codebase)
Cached Execution (85% savings):
/lazy-load # Subsequent runs use cached framework detection
# First run: ~2,500 tokens
# Cached run: ~400-600 tokens (85% savings)
Specific Component:
/lazy-load Dashboard # Target specific component
# Tokens: ~600-800 (focused implementation)
Overall: 50-60% token reduction (3,000-5,000 → 1,500-2,500 tokens)
This ensures effective lazy loading implementation across all major frameworks with measurable performance improvements and proper UX considerations.
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience