References

Beginner-friendly references for web development, with live, editable examples.

The HTML defer attribute

Attribute All modern browsers
In short

The HTML defer attribute holds a script until the document is parsed, then runs it before DOMContentLoaded fires. The file still downloads in parallel, and deferred scripts keep their source order even when a later one arrives first. It needs a src, as in <script defer src="app.js">.

Overview

The HTML defer attribute downloads an external script during parsing and runs it once parsing is complete. It belongs on <script> elements that have a src. Several deferred scripts run in the order they appear, even if a later file finishes downloading first.

Without defer or async, a classic script stops the parser at its tag until the file has downloaded and run. A deferred script instead sees the complete DOM, with document.readyState set to interactive, and DOMContentLoaded fires only after the last deferred script has run.

Deferred scripts also wait for stylesheets, although DOMContentLoaded itself doesn't. Because that event comes after the deferred scripts, a slow stylesheet in the head can hold up both.

The attribute only applies to scripts written in the page's HTML. A script you add with appendChild() ignores defer and can run as soon as it loads, even while the rest of the page is still being parsed.

Syntax

<script defer src="vendor.js"></script>
<script defer src="app.js"></script>

Values

Value
Holds the script until the page has been parsed.

Example

Live example
<p>Run order, with document.readyState at each step:</p>
<div role="status"><ol id="log"></ol></div>
<p>In the code, the deferred script sits between the two inline scripts and loads from a data: URL.</p>
<script>
function note(text) {
  const li = document.createElement('li');
  li.textContent = text + ' (' + document.readyState + ')';
  document.getElementById('log').append(li);
}
function deferredRan() {
  note('Deferred script');
}
note('First inline script');
document.addEventListener('DOMContentLoaded', function () {
  note('DOMContentLoaded');
});
</script>
<script defer src="data:text/javascript,deferredRan()"></script>
<script>note('Second inline script');</script>

Best practices

  • Put defer on a classic script with a src when it needs the page's elements or another script. List such scripts in the order they must run, and use async for standalone ones.
  • Place deferred scripts in the <head>. A script at the end of the body may start downloading late when the page is long and the network is slow.
  • Skip the DOMContentLoaded wrapper in deferred code, since the elements are already parsed when it runs. Such a listener still fires as usual, so the wrapper only adds a step.
  • Leave defer off module scripts. Modules are deferred already, and defer on them is invalid HTML.

Frequently asked questions

What is the defer attribute in a script tag?
The defer attribute downloads a script without stopping the parser and runs it after the HTML is parsed. Deferred scripts finish before the DOMContentLoaded event.
Should I write defer or defer=defer?
Both work, and so does an empty value, because defer is a boolean attribute. Any value turns it on, even false, so remove the attribute to turn defer off.
Is there a defer attribute for stylesheets?
No, link has no defer attribute, because defer is defined for script alone. A stylesheet whose media query doesn't match won't block rendering, so media is one way to keep it from holding up the page.
Do module scripts and defer scripts run in order?
Yes, defer scripts and module scripts without async share one queue and run in document order once parsing ends. A module listed first runs first, however long its download takes.
Does a defer script that fails to load stop the others?
No, the browser fires an error event on that defer script and moves on to the next one. DOMContentLoaded still fires once the remaining deferred scripts have run.
How do I defer an inline script?
The defer attribute has no effect on inline scripts, so move the code into a file loaded with defer. An inline script marked type=module also waits for parsing, and a DOMContentLoaded listener is another route.