The W3C Standard defines the Web Performance Timing API which is at least partially supported by all major browsers. Inspecting
window.performance.timing after a page has load returns timing data for each critical step during that page load.
The network tab in Chrome DevTools also shows page and resource load timings but the names (and in some cases the definitions) of the measured criteria differ from those returned by the Performance API.
The following diagram maps Performance API data to Chrome DevTools timings:
¹ Chrome DevTools (and documentation) defines TTFB as the time between the moment the request is sent and the moment the first byte of the response is received. Most other sources disagree with this definition, and consider DNS lookup time and connection time to also be part of the TTFB duration.
window.performance.getEntriesByType('resource') returns an array of benchmark timings for each resource (js, css, fonts. images, API calls etc.).
The HTML document has been loaded and parsed. This is the earliest possible time that the page is renderable. This event (and thus rendering) can be delayed if the page includes blocking resources.
The page and all dependent resources are fully loaded. This is a good time to inspect/post Performance API data.
Delay before processing request due to:
Stages of TTFB
As soon as the HTML has been parsed (DOMContentLoaded), the browser can begin the rendering cycle. Whenever the HTML parser encounters a blocking resource, DOMContentLoaded (and thus the start of the rendering cycle) is delayed.
Default <script> tags
By default all <script> tags are blocking. When the HTML parser encounters a script tag it will stop parsing HTML while it fetches and executes the script
<script> tags with async
Adding async to the <script> tag unblocks the HTML parser during script download. If the script is downloaded before HTML parsing is complete, parsing will be paused while the script is executed.
Use async when:
<script> tags with defer
Adding defer to the <script> tag also unblocks the HTML parser during script download. The script will not be executed until after the HTML parsing is complete.
Use defer when
The browser requires both a DOM and a CSSOM (CSS object model) before it can begin the rendering cycle—so by default the HTML parser treats all CSS resources as render blocking.
Inline CSS is will still block the HTML parser but inlining short, render-critical <style> directives can reduce the render blocking time because there is no data fetching or rule generation required.
Using a dynamic stylesheet generator like Styletron can significantly reduce time to DOMContentLoaded by only generating the subset of rules required for the current DOM
CSS links with media attribute
CSS links that include media types and/or media queries will conditionally be considered as non-blocking.
print.css will only be block rendering during print preview:
<link href="print.css" rel="stylesheet" media="print">
portrait.css will only be block rendering if the device is vertically oriented:
<link href="portrait.css" rel="stylesheet" media="orientation:portrait">
Referencing an external font file (via @fontface)
Fonts defined in external files are never render blocking. This is the case whether the style rule was defined via a CSS file, inline <style> header or inline style.
When a DOM element is assigned a style rule that references an externally defined font file, the browser will fetch that font. If such elements are present in the initial load, they will delay the load event but not the DomContentLoaded event.
If a font-referencing style rule is dynamically assigned after the initial load, the font will be lazy-loaded (even if the @fontface declaration is defined in the initial page load). Be aware that lazy loaded fonts have their own drawbacks. While the font is being fetched, the element will be invisible for up to three seconds (FOIC), after which it will render with the fallback font (FOUT). The FusionJS font loader (see below) can mitigate against this.
@fontface with an inline Data URI
By contrast inlining font data in the URI is always render-blocking regardless of whether the font is used on the first page load or later. Be wary of using of inling font data if you care about time-to-page-render.
Almost every resource type (including js, css, images and fonts) can also be loading using a link tag with the
rel attribute optionally set to
(Not yet well supported). Directs the resource to be fetched with the highest priority. It's useful for prioritizing those resources most critical for rendering.
While preload fetches can make your image or font show up quicker, they're also render blocking so will likely delay the time to DOMContentLoaded and the user will wait a little longer to see the initial rendering of the page. Mileage definitely varies so you should experiment with how well it works for you.
This is a lower priority hint that will fetch the resource during idle time and then cache for future use. This is useful for preloading resources for later pages, without delaying DOMContentLoaded. However it may delay the load complete event.
FusionJS Font Loader
FusionJS scaffolds with a font loader plugin and HOC which will autogenerate your @fontface declaration based on a font config file, allows you to control which fonts are preloaded vs. lazy loaded and mitigates against FOIC and FOUT.
The following timings illustrate typical time to DOMContentLoaded and time to load complete for four load strategies. This only covers cases where the required font is on the initial page. It doesn't cover lazily-loading fonts (see above).
Each strategy was tested loading a) 1 font b) 10 fonts. All fonts are identically sized.
Lato-Bold font (1 font / 10 fonts). Chrome 64 over "Slow 3G" connection.
|DomContentLoaded (ms)||load (ms)|
|no preload/prefetch||4049 / 4851||6657 / 14846|
|rel="preload"||4080 / 12810||4686 /12830|
|rel="prefetch"||4067 / 5478||6675 / 21296|
|inline data||4831 / 20848||4840 / 20869|
Images are also non-blocking by default, and share many of the load characteristics of fonts.
Example Page Load Times per Image-load strategy
The following timings illustrate typical time to DOMContentLoaded and time to load complete for four image load strategies. This doesn't cover lazily-loaded images.
Each strategy was tested loading a) 1 image b) 10 images. All images are identically sized
106kB JPEG (1 image / 10 images). Chrome 64 over "Slow 3G" connection
|DomContentLoaded (ms)||load (ms)|
|no preload/prefetch||4124 / 4126||8224 / 29051|
|rel="preload"||4147 / 4152||6518 / 26590|
|rel="prefetch"||4126 / 4138||8344 / 46374*|
|inline data||4951 / 29868||4951 / 29878|
* double loads img