Frontend Performance
Ship browser-side speed. Measure Core Web Vitals, then implement the change that moves the dominant one. This is not a general app profile.
Server CPU, p95 APIs, N+1 queries, and DB plans belong in performance-review. Stay on LCP, INP, CLS, waterfalls, images, bundles, and hydration.
Workflow
- Name the surface and the goal vital. Route or template, device class, and which of LCP / INP / CLS (or a JS-byte budget) is the SLO.
- Gather evidence. Prefer field (CrUX/RUM) over one lab run. Use a Lighthouse report, Perf-panel waterfall, long-task profile, or bundle analyzer when given. If none, prescribe the cheapest measurement first.
- Own the dominant vital. Name the LCP element, the long task or handler, the shifting node, or the blocking request. One owner.
- Implement the highest-impact browser change. Typical: priority + dimensions on the LCP image (never
loading="lazy"on it), route-levelimport(), defer non-critical JS, shrink the hydration surface, reserve space, font-display with fallback metrics. - Verify the same vital on the same device class. Do not pivot to a vanity Performance score.
Common owners
| Symptom | Often the owner |
|---|---|
| Bad LCP | late hero, render-blocking CSS/JS, webfont, HTML TTFB |
| Bad INP | long tasks, sync hydration, heavy click/input handlers |
| Bad CLS | unsized media, injected banners, font swap, late ads |
| Huge JS | no route split, unused deps, SSR + client duplicate |
| Slow first paint | blocking CSS/JS, long critical request chain |
Output format
## Frontend performance: <route or page>
**Goal vital / budget:** …
**Evidence used:** … (or: measurements to collect first)
**Field vs lab:** …
### Dominant bottleneck
Vital + owning element/request/script + confidence (low/med/high)
### Implementation
1. [high impact] … — expected effect, a11y impact, how to verify
2. …
### Do not do yet
Micro-opts and score theater that miss the owner.
### Verify
Same vital, same device class; what must not regress (a11y, SEO).
Rules
- Never invent Lighthouse scores, CrUX percentiles, or byte sizes.
- Measure, then fix one dominant Web Vital before a laundry list.
- Never drop
alt, focus rings, skip links, captions, orprefers-reduced-motionto save milliseconds. - Never claim a speedup percentage except as a labeled estimate.
- If HTML TTFB or the API is the owner, say so and hand origin CPU/DB to
performance-review; still name the page vital it hurts. - Prefer
width/heightoraspect-ratio,srcset/sizes, and fetch priority over custom lazy-load scripts. - Do not hide content with
display: noneor off-screen hacks that break keyboard, AT, or SEO and call it a win. - Distinguish lab (Lighthouse) from field (CrUX/RUM); prefer field when they disagree.
Edge cases
- No trace or Lighthouse: give a 3-step recipe (field vital → waterfall → long tasks / coverage) before rewrites.
- Backend-bound page: TTFB or API waterfalls →
performance-reviewfor the origin; do not pretend auseMemowill fix SQL. - SSR / hydration: ship less client JS; do not hydrate static trees; do not flash a non-keyboard first paint.
- "Make Lighthouse 100": refuse score chasing; pick the failing vital and a real budget (e.g. LCP under 2.5s on mid-tier mobile).
- Images vs CLS: never strip dimensions to “make it load faster.”