Optimizing web performance with Core Web Vitals, lazy loading, and caching strategies
# Web Performance Optimization for Google Antigravity
Optimize web performance with Google Antigravity's Gemini 3 engine. This guide covers Core Web Vitals, lazy loading, code splitting, and image optimization.
## Image Optimization Component
```typescript
// components/OptimizedImage.tsx
import Image from 'next/image';
import { useState } from 'react';
import { cn } from '@/lib/utils';
interface OptimizedImageProps {
src: string;
alt: string;
width: number;
height: number;
priority?: boolean;
className?: string;
sizes?: string;
}
export function OptimizedImage({
src,
alt,
width,
height,
priority = false,
className,
sizes = '(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw',
}: OptimizedImageProps) {
const [isLoaded, setIsLoaded] = useState(false);
const [hasError, setHasError] = useState(false);
if (hasError) {
return (
<div
className={cn(
'bg-gray-200 flex items-center justify-center',
className
)}
style={{ width, height }}
role="img"
aria-label={alt}
>
<span className="text-gray-400">Image unavailable</span>
</div>
);
}
return (
<div className={cn('relative overflow-hidden', className)}>
{/* Placeholder */}
{!isLoaded && (
<div
className="absolute inset-0 bg-gray-200 animate-pulse"
style={{ aspectRatio: `${width}/${height}` }}
/>
)}
<Image
src={src}
alt={alt}
width={width}
height={height}
sizes={sizes}
priority={priority}
loading={priority ? undefined : 'lazy'}
onLoad={() => setIsLoaded(true)}
onError={() => setHasError(true)}
className={cn(
'transition-opacity duration-300',
isLoaded ? 'opacity-100' : 'opacity-0'
)}
// Quality optimization
quality={85}
// Enable placeholder blur for large images
placeholder={width > 40 ? 'blur' : 'empty'}
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAIAAoDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAUH/8QAHxAAAgICAgMBAAAAAAAAAAAAAQIDBAARBRITITFB/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAYEQEBAQEBAAAAAAAAAAAAAAABAgADEf/aAAwDAQACEQMRAD8Ax"
/>
</div>
);
}
```
## Lazy Loading Hook
```typescript
// hooks/useLazyLoad.ts
import { useRef, useState, useEffect, useCallback } from 'react';
interface UseLazyLoadOptions {
threshold?: number;
rootMargin?: string;
triggerOnce?: boolean;
}
export function useLazyLoad<T extends HTMLElement>(
options: UseLazyLoadOptions = {}
) {
const { threshold = 0.1, rootMargin = '50px', triggerOnce = true } = options;
const ref = useRef<T>(null);
const [isInView, setIsInView] = useState(false);
const [hasTriggered, setHasTriggered] = useState(false);
useEffect(() => {
const element = ref.current;
if (!element || (triggerOnce && hasTriggered)) return;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsInView(true);
setHasTriggered(true);
if (triggerOnce) {
observer.unobserve(element);
}
} else if (!triggerOnce) {
setIsInView(false);
}
},
{ threshold, rootMargin }
);
observer.observe(element);
return () => observer.disconnect();
}, [threshold, rootMargin, triggerOnce, hasTriggered]);
return { ref, isInView };
}
// Usage
function LazyComponent() {
const { ref, isInView } = useLazyLoad<HTMLDivElement>();
return (
<div ref={ref}>
{isInView ? <HeavyComponent /> : <Placeholder />}
</div>
);
}
```
## Bundle Splitting
```typescript
// next.config.js
/** @type {import('next').NextConfig} */
module.exports = {
experimental: {
optimizePackageImports: [
'@heroicons/react',
'lodash',
'date-fns',
'@radix-ui/react-dialog',
],
},
webpack: (config, { isServer }) => {
// Split chunks more aggressively
if (!isServer) {
config.optimization.splitChunks = {
chunks: 'all',
cacheGroups: {
default: false,
vendors: false,
// Framework chunk
framework: {
name: 'framework',
chunks: 'all',
test: /[\\/]node_modules[\\/](react|react-dom|scheduler)[\\/]/,
priority: 40,
enforce: true,
},
// Library chunk
lib: {
test: /[\\/]node_modules[\\/]/,
name(module) {
const match = module.context.match(
/[\\/]node_modules[\\/](.*?)([\\/]|$)/
);
return `npm.${match[1].replace('@', '')}`;
},
priority: 30,
minChunks: 1,
reuseExistingChunk: true,
},
// Commons chunk
commons: {
name: 'commons',
minChunks: 2,
priority: 20,
},
},
};
}
return config;
},
};
```
## Virtual Scrolling
```typescript
// components/VirtualList.tsx
import { useVirtualizer } from '@tanstack/react-virtual';
import { useRef } from 'react';
interface VirtualListProps<T> {
items: T[];
height: number;
itemHeight: number;
renderItem: (item: T, index: number) => React.ReactNode;
overscan?: number;
}
export function VirtualList<T>({
items,
height,
itemHeight,
renderItem,
overscan = 5,
}: VirtualListProps<T>) {
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => itemHeight,
overscan,
});
return (
<div
ref={parentRef}
className="overflow-auto"
style={{ height, contain: 'strict' }}
>
<div
style={{
height: `${virtualizer.getTotalSize()}px`,
width: '100%',
position: 'relative',
}}
>
{virtualizer.getVirtualItems().map((virtualItem) => (
<div
key={virtualItem.key}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: `${virtualItem.size}px`,
transform: `translateY(${virtualItem.start}px)``,
}}
>
{renderItem(items[virtualItem.index], virtualItem.index)}
</div>
))}
</div>
</div>
);
}
```
## Performance Monitoring
```typescript
// lib/performance.ts
export function reportWebVitals(metric: {
name: string;
value: number;
id: string;
}) {
const { name, value, id } = metric;
// Report to analytics
if (process.env.NODE_ENV === 'production') {
fetch('/api/analytics/vitals', {
method: 'POST',
body: JSON.stringify({ name, value, id }),
headers: { 'Content-Type': 'application/json' },
keepalive: true,
});
}
// Log in development
if (process.env.NODE_ENV === 'development') {
console.log(`[Web Vital] ${name}: ${value}`);
}
}
// Measure custom metrics
export function measurePerformance(name: string, fn: () => void) {
const start = performance.now();
fn();
const duration = performance.now() - start;
performance.measure(name, {
start,
duration,
});
return duration;
}
// Long task detection
export function observeLongTasks(callback: (duration: number) => void) {
if ('PerformanceObserver' in window) {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
callback(entry.duration);
}
});
observer.observe({ entryTypes: ['longtask'] });
return () => observer.disconnect();
}
return () => {};
}
```
## Best Practices
Google Antigravity's Gemini 3 engine recommends these performance patterns: Optimize Largest Contentful Paint with priority loading. Minimize Cumulative Layout Shift with aspect ratios. Reduce First Input Delay with code splitting. Implement virtual scrolling for long lists. Monitor Core Web Vitals continuously.This Performance prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their performance implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For Performance projects, consider mentioning your framework version, coding style, and any specific libraries you're using.