web.dev: Web Vitals - discussion (2023-01-29)
The web.dev Web Vitals guide is one of the few performance resources that stays grounded in how pages feel. In this repo's vocabulary, it maps nicely to performance as posture: budgets, cadence, and making the user's experience visible through evidence rather than vibes.
Do you actually log vitals in app code, or rely on external tooling only? What evidence do you render so perf issues can be debugged from screenshots/traces? How do you balance performance posture with correctness posture (freshness, re-renders, derived state)?
Comments (14)
Back to latestWe log vitals in app code, but only in a "contract log" way (session id + route + posture).
The raw numbers without context are almost useless.
We emit a tiny perf log that ties vitals to route posture, so you can correlate spikes:
txt
[tips] vitals route=/dashboard lcp=2.4s inp=180ms cls=0.03 posture=warm cacheLane=cache
[tips] vitals route=/dashboard lcp=3.9s inp=410ms cls=0.12 posture=hot cacheLane=bypass reason=freshnessSwitch
The key is the reason + posture fields. Otherwise it's just numbers.
Counterpoint: logging vitals from app code can distort results if it adds work.
We do a very small sample and keep everything else in external tooling.
Agreed. We treat it as evidence, not as analytics.
If the evidence costs too much, the evidence isn't worth it.
The most practical thing for us was rendering a render-budget posture in the UI.
It turns perf from "we think" into "we decided".
CLS got better once we stopped letting derived summaries reflow unpredictably.
We reserved space for card summaries and kept the derived shape stable (even if values were updating).
INP/interaction issues often came from too much work in one render tick. We introduced a cool tick posture and logged the tick window:
txt
[tips] tick=cool windowMs=10000 reason=batterySaverCounterpoint: you can chase vitals and accidentally ruin correctness.
We had to decide when freshness matters more than perf and encode it as posture.
The biggest perf win was making the route shell stable and moving churn into derived cards that update predictably.
When the shell is stable, the page feels fast even when data is moving.
If you adopt evidence logs, beware of logging everything.
Only log transitions that map to user experience (posture, lane, identity).
Docs tie-in: the perf docs in React Tips treat budgets and posture as first-class, which matches web.dev pretty well.
Related: Performance and Rendering (Best Practices) and The App Router Mindset.
We also render a single "perf posture" badge in debug mode.
It prevents people from reporting perf bugs without mentioning the mode they're in.
LCP improved when we pre-derived the hero card output and avoided conditional layout swaps.
The output shape stayed constant; only values updated.
Counterpoint: sometimes the best fix is just less JS and less complexity.
Posture helps, but if the app is doing too much, posture won't save you.
True. We used the vitals logs to find where complexity was real vs imagined.
Then we deleted things. The most underrated optimization.
VItals are useful because they force you to define what "good" means.
Once you define it, you can encode it as a contract and log it like everything else.