“Slow” is not a diagnosis
When a page feels slow, the first useful question is not “Which framework is responsible?” It is: where did the time go? A request crosses several boundaries, and each boundary needs a different measurement.
| Stage | Typical work | Useful observation |
|---|---|---|
| Edge | Routing and cached response lookup | Cache status, age, and region |
| Server | Data fetch and HTML generation | Time to first byte |
| Network | Connection and transfer | Protocol and payload size |
| Browser | Parse, style, layout, and paint | Navigation timing and visual stability |
Start with a small measurement
const navigation = performance.getEntriesByType(
'navigation',
)[0] as PerformanceNavigationTiming
const serverWait = navigation.responseStart - navigation.requestStart
const download = navigation.responseEnd - navigation.responseStart
const interactivePath =
navigation.domContentLoadedEventEnd - navigation.startTime
console.table({ serverWait, download, interactivePath })
These values are clues, not verdicts. Run the measurement several times, keep the route constant, and note whether the response was a cache hit.
A practical investigation order
- Confirm whether meaningful HTML is rendered on the server.
- Record one cold request and at least three warm requests.
- Inspect response headers for cache identity, freshness, and region.
- Compare HTML size with the largest stylesheet and script.
- Test a real content page, not only the homepage.
- Repeat on a narrow viewport and a slower connection. Useful changes are often modest:
- Cache validated public content near the reader.
- Set explicit timeouts around upstream APIs.
- Avoid client code for components that never become interactive.
- Reserve image dimensions to prevent layout movement.
- Measure before and after each change. MDN documents PerformanceNavigationTiming. The important habit is tying every number to a concrete system boundary.
What does “100 ms” actually mean?
It is not a universal target. It is a question: what must be true for this URL to answer that quickly and still return correct, fresh-enough content?