Example 1: a server-rendered store that ignores early taps
Illustrative concept, fictional retailer. A Next.js storefront renders product pages on the server. Content appears quickly, but session recordings show shoppers tapping "Add to cart" several times before anything happens.
Before
- Lab score
- 42
- LCP
- 4.8 s
- INP
- 320 ms
- CLS
- 0.25
Hydrates everything at once
After
- Lab score
- 98
- LCP
- 1.1 s
- INP
- 76 ms
- CLS
- 0.02
Server-rendered, islands hydrate on view
Example 3: a Nuxt content site with layout shifts
Illustrative concept, fictional publisher. A Nuxt site renders articles on the server. Text jumps as the page loads, and the cookie banner pushes content down.
What the traces showed. Two causes stood out: the web font replaced its fallback with noticeably different metrics, and the cookie banner was inserted above the content after hydration. Both shifts happened during load, outside the 500 ms window after input that CLS excludes.
What the plan proposed.
- Add fallback font metric overrides so the swap barely changes line lengths.
- Reserve the banner's space in the server-rendered HTML, or overlay it instead of inserting it into the flow.
- Mark the article body and footer with
hydrate-neverwhere they contain no interactivity, which also reduced hydration time.
Example 4: a tool hub with a slow search box
Illustrative concept, fictional internal tool. An Angular app used by support staff has a search box that filters a list of a few thousand records as the user types. Staff say typing feels sticky.
What the traces showed. Each key press ran the filter over the full list and re-rendered every row, inside the same task as the input event. On a throttled profile, individual key presses took several hundred milliseconds to paint.
What the attribution showed. Most slow INP interactions targeted the search input, with processing duration as the dominant phase. Typing produces many interactions per visit, so this one control set the page's INP.
What the plan proposed.
- Update the input's own value immediately and debounce the filtering slightly, so typing paints at once.
- Run the filter in chunks with a yield between them, or in a Web Worker for the largest lists.
- Render only the visible rows with a virtual list.
- Check that the list's change detection runs only for rows whose data changed.
This example shows why typing is worth testing: every key press is an interaction, and a control used dozens of times per visit can dominate the metric.
How to read these examples
Each example follows the same order as a real report: the symptom users describe, what the traces showed, what field data or attribution suggested, and the plan. The order matters. Traces show what can happen on a test device; field data shows what does happen for real users; the plan should only follow when both point the same way. Where they disagree, a real report says so and explains which evidence we weighted more.
You may notice the examples never claim a business result such as more sales or sign-ups. Real reports avoid those claims too, unless your own analytics measured them, because performance is one of many things that change between two periods.
What the examples leave out on purpose
Real engagements contain things these pages cannot show: the specific files and components involved, screenshots of traces from a real build, and the conversations in which a team explains why something was built the way it was. That context often changes the plan. A heavy component may exist because of a contractual requirement; a slow API may be owned by another team with its own roadmap. A good review works within those constraints instead of pretending they are not there, and the findings say which recommendations depend on decisions outside the frontend team's control.
Patterns behind the examples
The three scenarios illustrate the patterns we write about most:
- Too much hydrated at once (Example 1). Covered in our hydration guide and React Server Components guide.
- Long handlers on the main thread (Example 2). Covered in breaking up long tasks and debugging INP with Long Animation Frames.
- Layout shifts from fonts and late content (Example 3). Covered in asset and font loading and the Vue and Nuxt hydration guide.
If one of these sounds like your app, the linked guides are a good place to start before contacting anyone.
Questions to ask of any example
Whoever shows you an example of performance work, ours included, a few questions separate a useful example from marketing: what device and network profile was used, which build was measured, over what period field data was collected, and what else changed between the before and after. If those answers are missing, treat the numbers as decoration.
What every real report adds
Real reports include what these examples cannot: the trace files themselves, the exact build and date measured, the device and network profiles used, the field data window, and the code locations involved. They also state uncertainty plainly. If we expect a change to help but cannot size the effect until it ships, the report says so.