DecisionLoad JavaScript as external resource with defer attribute

accepted

Loading 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 the defer attribute, the initial rendering of the page could be significantly delayed on devices with older or slower processors.

  • Avoid using both async and defer on a <script> tag as defer already provides all the benefits of async 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.

Additional Resources


Andy Blum, Aubrey Sambor, Chris DeLuca, Mateu Aguiló Bosch, Sally Young

Decided on