Decision Load JavaScript as external resource with defer attribute
acceptedLoading JavaScript as deferred has the best default performance
Decision
When loading external JavaScript files in HTML, they will have the defer
attribute.
<script src="/path/to/file.js" defer>
When adding JavaScript files with Drupal Libraries, this will require adding the defer
attribute on a per-file basis:
my_library:
version: 1.0.0
js:
js/my-script.js:
attributes:
defer: true
Context
Adding defer
to <script src="...">
tags will create the most performant page-loading experience:
-
The
async
attribute tells the browser to continue parsing HTML while the JavaScript file is downloading, but to allow the script to interrupt parsing for execution. -
The
defer
attribute tells the browser to avoid executing the JavaScript until the entire document has been parsed. Scripts with the defer attribute will be fetched asynchronously by default. Without thedefer
attribute, the initial rendering of the page could be significantly delayed on devices with older or slower processors. -
Avoid using both
async
anddefer
on a<script>
tag asdefer
already provides all the benefits ofasync
and adding both may result in inconsistent or undesirable cross-browser behavior.
Exceptions
There are rare instances when JavaScript should be added into the body of the HTML document, such as removing a no-js
class from the <html>
element. In this case, inline blocking JavaScript is preferable to avoid cumulative layout shifts and perceived reflows of the document. For example, in the code below, the HTML document is sent to the browser with a no-js
class to prevent displaying portions of the page that require JavaScript. By adding an inline style early in the document, we can prevent the browser from rendering the page once with the no-js
class and then reflowing when no-js
is removed.
<html class="no-js">
<head>
<script>
document.documentElement.classList.remove('no-js');
</script>
<style>
.no-js #app {
display:none;
}
</style>
</head>
<body>
</body>
</html>
Consequences
Requesting & executing JavaScript in this manner should lead to more performant page loading and the most consistent script execution since scripts will not run until the entire DOM is in place. This, in turn, will create a better experience for end-users and a better score on objective performance metrics.