References

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

The HTML async attribute

Attribute All modern browsers
In short

The HTML async attribute runs a script as soon as it is available, even in the middle of parsing. With <script async src="analytics.js">, the parser never waits for the file. Async scripts run as each one finishes loading, and external scripts added with createElement() are async by default.

Overview

The HTML async attribute lets a script run once it is ready, without waiting for parsing to end. Classic <script> elements need a src for it to apply, while module scripts accept it with or without one.

Async scripts don't wait for parsing to finish or for each other. Whichever finishes downloading first runs first, sometimes before the page is fully parsed. The DOMContentLoaded event doesn't wait for them, but the window load event does.

Every script element starts with an internal force async flag set to true, and the parser clears it on scripts in the page's markup. A script you make with document.createElement() keeps the flag, so its async property reads true with no attribute present.

On a module script, async changes when the code runs. A module normally waits for parsing to finish, the way a deferred script does. With async, it runs as soon as the module and every module it loads through import statements are ready.

Syntax

<script async src="analytics.js"></script>
<script type="module" async src="widget.js"></script>

Values

Value
Lets the script run once downloaded, without waiting for parsing.

Example

Live example
<p id="order" role="status"></p>
<button type="button" id="make">Create a script element</button>
<p><label><input type="checkbox" id="off"> Set its async property to false</label></p>
<p id="msg" role="status"></p>
<p>Press the button, then tick the box and press it again.</p>
<script>
const order = [];
function ran(name) {
  order.push(name);
  document.getElementById('order').textContent = 'Run order here: ' + order.join(', ') + '.';
}
ran('inline script');
document.addEventListener('DOMContentLoaded', function () {
  ran('DOMContentLoaded');
});
const msg = document.getElementById('msg');
msg.textContent = 'The inline script tag reports async as ' + document.currentScript.async + '.';
document.getElementById('make').addEventListener('click', function () {
  const script = document.createElement('script');
  if (document.getElementById('off').checked) {
    script.async = false;
  }
  const attr = script.hasAttribute('async') ? 'an' : 'no';
  msg.textContent = 'A new script element reports async as ' + script.async + ', with ' + attr + ' async attribute.';
});
</script>
<script async src="data:text/javascript,ran('async')"></script>

Best practices

  • Choose async for a standalone script, one that depends on no other script, such as an analytics tag.
  • Load a library and the code that calls it with defer instead. Two async scripts can run in either order, so a plugin might run before its library and throw an error.
  • In an async script, check document.readyState before adding a DOMContentLoaded listener. The event may have fired already, and the setup code may then never run.
  • Keep document.write() out of async scripts. The call is ignored, and Chrome logs a warning about an asynchronously loaded script.

Frequently asked questions

When does a script with the async attribute run?
A script with the async attribute runs as soon as it can once its file arrives. The parser doesn't wait for the download, so that can happen before or after the rest of the page has been parsed.
How is async different from defer?
An async script runs as soon as it is available, in no fixed order. A defer script waits until parsing ends and keeps the order of the script tags.
Does an async script block rendering?
An async script doesn't block the page while downloading, but it blocks rendering while it runs. The rest of the page waits to be processed and shown until the script finishes.
What happens if a script has both async and defer?
A script with both async and defer behaves as async. The pair is allowed so that old browsers that only know defer fall back to it instead of blocking the parser.
Does async work on inline scripts?
The async attribute does nothing on an inline classic script, and validator.nu reports it as an error. On an inline module script it works, and the module then runs without waiting for parsing to finish.
How do I turn off async for scripts added with JavaScript?
Set each script's async property to false before you insert it, and the scripts then run in insertion order. They still download in parallel, but a slow first file holds back the ones after it.