How do lazy loading and code splitting improve performance?
Lazy loading defers work until it is needed. For images, loading="lazy" and decoding="async" defer off-screen loads, but never lazy-load the LCP image because it delays the largest paint. For iframes and heavy media, load on intersection with an IntersectionObserver or loading="lazy".
Code splitting breaks a JavaScript bundle into chunks loaded on demand. Bundlers create a chunk per dynamic import:
const Chart = lazy(() => import('./Chart'));
Route-based splitting is the highest-value pattern, because users only download the page they visit. React.lazy, Vue's defineAsyncComponent and Next.js dynamic imports integrate chunk loading with Suspense boundaries. Also split large vendors and load polyfills conditionally with module and nomodule.
The trade-off is extra network round trips, so preload or prefetch likely next routes and avoid creating hundreds of tiny chunks. Combine splitting with a fast CDN and long-lived caching of hashed filenames.