HTML Guides for async
Learn how to identify and fix common HTML validation errors flagged by the W3C Validator — so your pages are standards-compliant and render correctly across every browser. Also check our Accessibility Guides.
The HTML specification defines boolean attributes as attributes whose presence indicates a true state and whose absence indicates false. According to the WHATWG HTML standard, a boolean attribute may only have three valid representations:
- The attribute name alone (e.g., async)
- The attribute with an empty string value (e.g., async="")
- The attribute with a value matching its own name, case-insensitively (e.g., async="async")
Any other value — such as async="true", async="1", async="yes", or async="false" — is invalid HTML and will trigger this validation error. This is a common misunderstanding because developers often assume boolean attributes work like boolean values in programming languages, where you’d assign true or false.
Why this matters
While most browsers are lenient and will treat any value of async as the true state (since the attribute is present regardless of its value), using invalid values creates several problems:
- Standards compliance: Invalid HTML may cause issues with strict parsers, validators, or tools that process your markup.
- Misleading intent: Writing async="false" does not disable async behavior — the attribute is still present, so the browser treats it as enabled. This can lead to confusing bugs where a script behaves asynchronously even though the developer intended otherwise.
- Maintainability: Other developers reading the code may misinterpret async="false" as actually disabling async loading.
To disable async behavior, you must remove the attribute entirely rather than setting it to "false".
How async works
For classic scripts with a src attribute, the async attribute causes the script to be fetched in parallel with HTML parsing and executed as soon as it’s available, without waiting for the document to finish parsing.
For module scripts (type="module"), the async attribute causes the module and all its dependencies to be fetched in parallel and executed as soon as they are ready, rather than waiting until the document has been parsed (which is the default deferred behavior for modules).
Examples
❌ Invalid: arbitrary values on async
<!-- Bad: "true" is not a valid boolean attribute value -->
<script async="true" src="app.js"></script>
<!-- Bad: "1" is not a valid boolean attribute value -->
<script async="1" src="analytics.js"></script>
<!-- Bad: "yes" is not a valid boolean attribute value -->
<script async="yes" src="tracker.js"></script>
<!-- Bad and misleading: this does NOT disable async -->
<script async="false" src="app.js"></script>
✅ Valid: correct boolean attribute usage
<!-- Preferred: attribute name alone -->
<script async src="app.js"></script>
<!-- Also valid: empty string value -->
<script async="" src="app.js"></script>
<!-- Also valid: value matching attribute name -->
<script async="async" src="app.js"></script>
<!-- Correct way to disable async: remove the attribute -->
<script src="app.js"></script>
✅ Valid: async with module scripts
<script async type="module" src="app.mjs"></script>
<script async type="module">
import { init } from './utils.mjs';
init();
</script>
This same rule applies to all boolean attributes in HTML, including defer, disabled, checked, required, hidden, and others. When in doubt, use the attribute name on its own with no value — it’s the cleanest and most widely recognized form.
There is an illegal double quote character (") at the end of the src attribute value in your <script> tag, which causes the attribute to be invalid.
Attribute values must not include unescaped or stray quote characters (" or ') inside them, as this breaks attribute parsing and results in invalid HTML. The src attribute for a <script> tag should contain a properly encoded URL without any stray quotes or illegal characters. In your case, a double quote has accidentally been included before the closing quote of the attribute.
Correct usage for a <script> tag with the async attribute is:
<script src="https://example.com/js/jquery-3.6.0.min.js?ver=6.8.2" async></script>
Incorrect example with the error (shows the issue):
<script src="https://example.com/js/jquery-3.6.0.min.js?ver=6.8.2" async""></script>
Make sure there are no stray characters in your attribute values and that boolean attributes like async do not have values—it should simply be present, as in async, not async"" or async="async".
If you need a full, minimal HTML document to validate, use:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Valid Script Tag Example</title>
</head>
<body>
<script src="https://example.com/js/jquery-3.6.0.min.js?ver=6.8.2" async></script>
</body>
</html>
Double-check your HTML source code to ensure there are no accidental typos or misplaced quote marks in your tag attributes.
The async attribute tells the browser to download and execute a script without blocking HTML parsing. For external scripts (those with a src attribute), this means the browser can continue parsing the page while fetching the file, then execute the script as soon as it’s available. For inline module scripts (type="module"), async changes how the module’s dependency graph is handled — the module and its imports execute as soon as they’re all ready, rather than waiting for HTML parsing to complete.
For a classic inline script (no src, no type="module"), there is nothing to download asynchronously. The browser encounters the code directly in the HTML and executes it immediately. Applying async in this context is meaningless and contradicts the HTML specification, which is why the W3C validator flags it as an error.
Beyond standards compliance, using async incorrectly can signal a misunderstanding of script loading behavior, which may lead to bugs. For example, a developer might mistakenly believe that async on an inline script will defer its execution, when in reality it has no effect and the script still runs synchronously during parsing.
How to Fix
You have several options depending on your intent:
- If the script should be external, move the code to a separate file and reference it with the src attribute alongside async.
- If the script should be an inline module, add type="module" to the <script> tag. Note that module scripts are deferred by default, and async makes them execute as soon as their dependencies are resolved rather than waiting for parsing to finish.
- If the script is a plain inline script, simply remove the async attribute — it has no practical effect anyway.
Examples
❌ Invalid: async on a classic inline script
<script async>
console.log("Hello, world!");
</script>
This triggers the validator error because there is no src attribute and the type is not "module".
✅ Fixed: Remove async from the inline script
<script>
console.log("Hello, world!");
</script>
✅ Fixed: Use async with an external script
<script async src="app.js"></script>
The async attribute is valid here because the browser needs to fetch app.js from the server, and async controls when that downloaded script executes relative to parsing.
✅ Fixed: Use async with an inline module
<script async type="module">
import { greet } from "./utils.js";
greet();
</script>
This is valid because module scripts have a dependency resolution phase that can happen asynchronously. The async attribute tells the browser to execute the module as soon as all its imports are resolved, without waiting for the document to finish parsing.
❌ Invalid: async with type="text/javascript" (not a module)
<script async type="text/javascript">
console.log("This is still invalid.");
</script>
Even though type is specified, only type="module" satisfies the requirement. The value "text/javascript" is the default classic script type and does not make async valid on an inline script.
Ready to validate your sites?
Start your free trial today.