| Metric | Target | Status |\n|--------|--------|--------|\n| LCP (Largest Contentful Paint) | <2.5s | ✅ Critical |\n| FID (First Input Delay) | <100ms | ✅ Critical |\n| CLS (Cumulative Layout Shift) | <0.1 | ✅ Critical |\n| TTFB (Time to First Byte) | <600ms | 🎯 Target |\n| DCL (DOM Content Loaded) | <2s | 🎯 Target |\n\n### Bundle Metrics\n\n| Metric | Target | Monitor |\n|--------|--------|----------|\n| Initial Bundle | <100KB gzipped | ✅ Action on +10% |\n| JavaScript | <80KB gzipped | ✅ Per dependency |\n| CSS | <20KB gzipped | ✅ Per component |\n| Images | <500KB per page | ✅ Image optimization |\n\n## Code Splitting\n\n### Route-Based Splitting\n\ntypescript\n// src/router.tsx\nimport { lazy, Suspense } from 'react';\nimport { createBrowserRouter } from 'react-router-dom';\n\n// Split at route level\nconst ProductsPage = lazy(() => import('./pages/ProductsPage'));\nconst CheckoutPage = lazy(() => import('./pages/CheckoutPage'));\nconst AdminPage = lazy(() => import('./pages/admin/AdminPage'));\nconst NotFoundPage = lazy(() => import('./pages/NotFoundPage'));\n\nconst LoadingFallback = () => (\n <div className=\"page-loader\">\n <Spinner />\n <p>Loading...</p>\n </div>\n);\n\nexport const router = createBrowserRouter([\n {\n path: '/',\n element: <Layout />,\n children: [\n {\n path: 'products',\n element: (\n <Suspense fallback={<LoadingFallback />}>\n <ProductsPage />\n </Suspense>\n ),\n },\n {\n path: 'checkout',\n element: (\n <Suspense fallback={<LoadingFallback />}>\n <CheckoutPage />\n </Suspense>\n ),\n },\n {\n path: 'admin',\n element: (\n <ProtectedRoute>\n <Suspense fallback={<LoadingFallback />}>\n <AdminPage />\n </Suspense>\n </ProtectedRoute>\n ),\n },\n {\n path: '*',\n element: <NotFoundPage />,\n },\n ],\n },\n]);\n\n\n### Component-Based Splitting\n\ntypescript\n// Heavy chart component\nconst Chart = lazy(() => import('./components/Chart'));\n\n// Dashboard with lazy-loaded chart\nexport const Dashboard = () => (\n <Suspense fallback={<ChartSkeleton />}>\n <Chart />\n </Suspense>\n);\n\n\n## Rendering Optimization\n\n### Memoization: React.memo\n\ntypescript\n// Prevents re-renders when props haven't changed\ninterface ListItemProps {\n item: Item;\n onClick: (id: string) => void;\n}\n\n// Without memoization: re-renders on parent update\nconst ListItemBad = ({ item, onClick }: ListItemProps) => (\n <li onClick={() => onClick(item.id)}>{item.name}</li>\n);\n\n// With memoization: only re-renders if props change\nconst ListItem = React.memo<ListItemProps>(\n ({ item, onClick }) => (\n <li onClick={() => onClick(item.id)}>{item.name}</li>\n ),\n // Custom comparison (optional)\n (prevProps, nextProps) =>\n prevProps.item.id === nextProps.item.id &&\n prevProps.onClick === nextProps.onClick\n);\n\n\n### Selector Memoization: createSelector\n\ntypescript\n// Without memoization - creates new object every time\nconst cartInfo = useAppSelector((state) => ({\n items: state.cart.items,\n total: state.cart.total,\n})); // ❌ New object reference → re-render\n\n// With memoization - same object if data unchanged\nconst selectCartInfo = createSelector(\n [(state: RootState) => state.cart.items, (state: RootState) => state.cart.total],\n (items, total) => ({ items, total })\n);\n\nconst cartInfo = useAppSelector(selectCartInfo); // ✅ Memoized\n\n\n### Callback Memoization: useCallback\n\ntypescript\nexport const SearchBox = ({ onSearch }: { onSearch: (query: string) => void }) => {\n const [query, setQuery] = useState('');\n\n // Without memoization - new function every render\n const handleSearch = (term: string) => {\n performSearch(term);\n }; // ❌ New reference → child re-renders\n\n // With memoization - same function reference\n const handleSearch = useCallback((term: string) => {\n performSearch(term);\n }, []); // ✅ Memoized\n\n return (\n <input\n value={query}\n onChange={(e) => handleSearch(e.target.value)}\n />\n );\n};\n\n\n### Value Memoization: useMemo\n\ntypescript\nexport const ExpensiveChart = ({ data }: { data: Point[] }) => {\n // Without memoization - recalculates every render\n const processedData = data.map(point => ({...})); // ❌ Expensive\n\n // With memoization - recalculates only when data changes\n const processedData = useMemo(\n () => data.map(point => ({...})),\n [data]\n ); // ✅ Memoized\n\n return <Chart data={processedData} />;\n};\n\n\n## Image Optimization\n\n### Responsive Images with Next-Gen Formats\n\ntypescript\n// Using modern image component\ninterface OptimizedImageProps {\n src: string;\n alt: string;\n width: number;\n height: number;\n priority?: boolean;\n}\n\nexport const OptimizedImage: React.FC<OptimizedImageProps> = ({\n src,\n alt,\n width,\n height,\n priority = false,\n}) => {\n // Generate srcset variants\n const srcSet = `\n ${src}?w=640&q=80 640w,\n ${src}?w=1280&q=80 1280w,\n ${src}?w=1920&q=80 1920w\n `;\n\n return (\n <picture>\n {/* WebP format for modern browsers */}\n <source\n srcSet={srcSet.replace(/\\.(jpg|png)/g, '.webp')}\n type=\"image/webp\"\n />\n {/* Fallback format */}\n <img\n src={src}\n srcSet={srcSet}\n alt={alt}\n width={width}\n height={height}\n loading={priority ? 'eager' : 'lazy'}\n decoding={priority ? 'auto' : 'async'}\n />\n </picture>\n );\n};\n\n// Usage\n<OptimizedImage\n src=\"/products/laptop.jpg\"\n alt=\"Laptop\"\n width={400}\n height={300}\n priority // Above the fold\n/>\n\n\n### Image Lazy Loading\n\ntypescript\n// Native lazy loading\n<img\n src=\"/images/product.jpg\"\n loading=\"lazy\"\n alt=\"Product\"\n/>\n\n// Intersection Observer for more control\nexport const LazyImage = ({ src, alt }: { src: string; alt: string }) => {\n const imgRef = useRef<HTMLImageElement>(null);\n const [loaded, setLoaded] = useState(false);\n\n useEffect(() => {\n const observer = new IntersectionObserver(([entry]) => {\n if (entry.isIntersecting && imgRef.current) {\n imgRef.current.src = src;\n setLoaded(true);\n observer.unobserve(imgRef.current);\n }\n });\n\n if (imgRef.current) {\n observer.observe(imgRef.current);\n }\n\n return () => observer.disconnect();\n }, [src]);\n\n return (\n <img\n ref={imgRef}\n alt={alt}\n className={loaded ? 'loaded' : 'loading'}\n />\n );\n};\n\n\n## CSS Optimization\n\n### Tailwind CSS\n\ntypescript\n// Tailwind provides utility-first CSS with minimal overhead\n// Only used classes are included in production build\n\n// ✅ Good: Uses Tailwind utilities\n<div className=\"flex items-center justify-between p-4 bg-white rounded-lg shadow\">\n <span className=\"text-lg font-semibold text-gray-900\">{title}</span>\n <button className=\"px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600\">\n Action\n </button>\n</div>\n\n// ❌ Avoid: Inline styles or CSS-in-JS\n<div style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '1rem',\n // ...\n}}>\n\n\n### CSS Containment\n\ncss\n/* Improve rendering performance for isolated components */\n.card {\n contain: layout style paint;\n /* Lets browser optimize rendering independently */\n}\n\n.list-item {\n contain: content;\n /* Only affects own content, not layout of outer page */\n}\n\n\n## Bundle Analysis\n\n### Vite Bundle Analyzer\n\nbash\n# Add to package.json\n\"build:analyze\": \"vite build --mode analyze\"\n\n# Then run\nnpm run build:analyze\n\n\n### Identifying Large Dependencies\n\ntypescript\n// Use dynamic imports for heavy libraries\nconst lodash = import('lodash'); // Only load when needed\n\n// Or use lighter alternatives\nimport { debounce } from 'lodash-es'; // Tree-shakeable\n\n\n## Caching Strategy\n\n### Browser Caching\n\nhttp\n# index.html - revalidate frequently\nCache-Control: public, max-age=3600\n\n# Assets (CSS, JS) - cache long, use hash in filename\nCache-Control: public, max-age=31536000\n\n# Images - cache moderately\nCache-Control: public, max-age=604800\n\n# API responses - RTK Query handles this\n\n\n## Monitoring & Debugging\n\n### Performance Monitoring\n\ntypescript\n// src/lib/performance.ts\nexport const recordWebVitals = () => {\n // Largest Contentful Paint\n if ('PerformanceObserver' in window) {\n const observer = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n console.log('LCP:', entry.startTime);\n // Send to analytics\n analytics.track('Performance', {\n metric: 'LCP',\n value: entry.startTime,\n });\n }\n });\n\n observer.observe({ entryTypes: ['largest-contentful-paint'] });\n }\n\n // First Input Delay\n if ('PerformanceObserver' in window) {\n const observer = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n console.log('FID:', entry.processingDuration);\n analytics.track('Performance', {\n metric: 'FID',\n value: entry.processingDuration,\n });\n }\n });\n\n observer.observe({ entryTypes: ['first-input'] });\n }\n};\n\n\n### React DevTools Profiler\n\ntypescript\n// Wrap component for profiling\nimport { Profiler } from 'react';\n\nconst onRenderCallback = (\n id,\n phase,\n actualDuration,\n baseDuration,\n startTime,\n commitTime,\n interactions\n) => {\n console.log(`${id} (${phase}) took ${actualDuration}ms`);\n};\n\n<Profiler id=\"ProductList\" onRender={onRenderCallback}>\n <ProductList />\n</Profiler>\n\n\n## Performance Checklist\n\n- [ ] LCP <2.5s\n- [ ] FID <100ms\n- [ ] CLS <0.1\n- [ ] Bundle size <100KB gzipped\n- [ ] Images lazy-loaded\n- [ ] Routes code-split\n- [ ] Selectors memoized\n- [ ] Components memoized\n- [ ] Lighthouse score >90\n- [ ] Zero Core Web Vitals warnings\n