ESC

Type to search the knowledge base.

Resource Timing API

PerformanceResourceTiming for every subresource: durations, transferSize, initiatorType, and RUM waterfalls.

advanced3 min read
  • browser
  • resource-timing
  • performance
  • network

Resource Timing exposes a PerformanceResourceTiming entry per fetched subresource (scripts, images, CSS, fetch/XHR, etc.). It powers custom waterfalls, CDN debugging, and “which third party ate our LCP budget?” analyses without HAR exports from every user.

Docs: MDN Resource Timing, Resource Timing Level 2.

Read entries

const resources = performance.getEntriesByType('resource');

for (const r of resources) {
  console.log({
    name: r.name,
    initiatorType: r.initiatorType, // script, img, css, fetch, ...
    duration: r.duration,
    transferSize: r.transferSize,
    encodedBodySize: r.encodedBodySize,
    decodedBodySize: r.decodedBodySize,
    protocol: r.nextHopProtocol,
  });
}

Stream with observer:

new PerformanceObserver((list) => {
  for (const e of list.getEntries()) sendResource(e);
}).observe({ type: 'resource', buffered: true });

Timing phases (same family as Navigation Timing)

startTime → redirect → worker → dns → connect → request → responseStart → responseEnd

Cross-origin resources without Timing-Allow-Origin hide many detailed timestamps (zeroed) — you still get limited data.

Timing-Allow-Origin: https://app.example.com

CDNs should send TAO if you need real DNS/connect/TTFB splits for cross-origin assets.

Cache inference

Signal Interpretation
transferSize === 0 and decoded > 0 Often cache or SW (heuristic)
encodedBodySize === 0 Possible cache/CORS opacity cases
Large duration, small body Latency / queueing

Treat heuristics carefully under service workers.

Building a mini waterfall

function toRow(r) {
  return {
    url: r.name,
    start: r.startTime,
    end: r.responseEnd,
    type: r.initiatorType,
    size: r.transferSize,
  };
}

Sort by startTime to visualize contention. Compare to DevTools Network panel when debugging locally — Network panel.

Privacy

Resource URLs can contain tokens in query strings. Strip sensitive query keys before beacons. Cap the number of resources reported per page.

Soft navigations

SPA route changes don’t clear the resource buffer the way full navigations do. Clear marks/measures thoughtfully and attribute resources to “soft nav IDs” if you build SPA RUM.

Interview out-loud

“Resource Timing gives per-subresource phases, sizes, and initiatorType. Cross-origin detail needs Timing-Allow-Origin. I use it for RUM waterfalls and third-party cost, scrubbing sensitive URLs before send.”

Third-party scoreboard

Aggregate transferSize and duration by host in RUM. Publish a monthly “third-party tax” chart. Teams fight harder for removal when the number is visible in dollars of LCP and lost conversions, not only in a Lighthouse screenshot.

Worker and CORS opacity

Opaque responses from no-cors requests expose limited timing and size data by design. For first-party CDNs you control, send Timing-Allow-Origin matching your app origin so dashboards show real DNS/connect/TTFB splits. Without TAO, your “slow image” investigation is mostly guesswork beyond total duration.

Further depth

Teams often under-invest in this topic until an incident or CWV regression. Schedule a one-hour drill: reproduce the failure mode in DevTools, list the top three mitigations for your stack, and file tickets with owners. Revisit after the next major feature that touches networking, rendering, auth, or third parties — those are the moments regressions land. Keep primary documentation links in the runbook so on-call is not searching chat history at 2am.

Concrete artifacts to leave behind: a short architecture note, a CI assertion or header snapshot, and a dashboard panel (lab or field) that would have caught the last bug. Teaching the rest of the team the mental model matters as much as the one-line fix.

Further reading

Related guides