Web Performance Guide

Code Splitting

// Dynamic import (React) const HeavyChart = React.lazy(() => import('./HeavyChart')); function App() { return ( <React.Suspense fallback={<Spinner />}> {showChart && <HeavyChart />} </React.Suspense> ); } // Route-based splitting (React Router) const Dashboard = React.lazy(() => import('./pages/Dashboard')); const Profile = React.lazy(() => import('./pages/Profile')); // Vite dynamic import const { default: module } = await import('./heavy-module.js'); // Webpack magic comments const module = await import(/* webpackChunkName: "analytics" */ './analytics');

Rendering Performance

// Avoid layout thrashing โ€” batch DOM reads and writes // BAD: interleaved read/write el1.style.width = el2.offsetWidth + 'px'; // triggers reflow el3.style.height = el4.offsetHeight + 'px'; // triggers reflow again // GOOD: batch reads, then writes const w = el2.offsetWidth; // read const h = el4.offsetHeight; // read el1.style.width = w + 'px'; // write el3.style.height = h + 'px'; // write // Use requestAnimationFrame for animations function animate() { element.style.transform = `translateX(${pos}px)`; pos += 1; requestAnimationFrame(animate); } // Use CSS transform/opacity (GPU composited) // GOOD: transform, opacity, filter // BAD: top, left, width, height (trigger layout)

Bundle Optimization

// Vite config โ€” manual chunk splitting // vite.config.js export default { build: { rollupOptions: { output: { manualChunks: { vendor: ['react', 'react-dom'], charts: ['recharts'], utils: ['lodash-es', 'date-fns'] } } } } } // Analyze bundle size npx vite-bundle-visualizer npx webpack-bundle-analyzer stats.json // Import only what you need import { debounce } from 'lodash-es'; // tree-shakeable // NOT: import _ from 'lodash';

Performance Metrics Reference

MetricTargetTool
LCP< 2.5sLighthouse, CrUX
INP< 200msLighthouse, CrUX
CLS< 0.1Lighthouse, CrUX
TTFB< 200msWebPageTest
FCP< 1.8sLighthouse
JS Bundle Size< 170KB (gzipped)Bundlesize, bundlephobia
Total Page Weight< 1MBChrome DevTools, GTmetrix