writing

How bundle size drives Core Web Vitals (especially LCP)

A practical map from JavaScript bytes to LCP, INP, and CLS — why a heavier bundle quietly degrades the metrics Google ranks on, and how to watch it.

Core Web Vitals are the user-experience metrics Google folds into ranking: LCP (loading), INP (interactivity), and CLS (visual stability). Bundle size touches all three, but its grip on LCP and INP is the tightest.

LCP: the bundle blocks the paint

Largest Contentful Paint measures when the biggest element in the viewport renders. On a client-rendered app, that element often cannot paint until the JavaScript that produces it has downloaded, parsed, and run. A heavier bundle pushes LCP out three ways:

  • Network — more bytes to download, worse on slow connections.
  • Parse/compile — the main thread is busy turning script into executable code.
  • Execution — framework boot + hydration runs before content appears.

The target is LCP < 2.5s. Every extra hundred kilobytes of render-critical JavaScript eats into that budget on real mobile hardware.

INP: big bundles mean long tasks

Interaction to Next Paint measures responsiveness. Large scripts execute as long tasks that block the main thread, so a tap during hydration feels stuck. Shipping less, and deferring what you can, keeps the main thread free to answer the user. Target: INP < 200ms.

CLS: indirect, but real

Cumulative Layout Shift is mostly a layout problem, but late-loading, render-blocking scripts that inject content after first paint cause shifts. Smaller, better-sequenced bundles shift less.

Watch the input, not just the output

Lighthouse gives you the output (an LCP number) at a point in time. Bundle size is the input you can attribute to a specific commit. Track both:

When LCP regresses, the first question is always "what got bigger?" Bundle tracking answers it before you have to guess.