defer attributes exist to help you make your website load faster. This article explains what these attributes do and when you should use them.
What problem do the async and defer keywords address?
script tags are render-blocking, which means that the browser won’t display any page content until after the script has run. When the browser’s HTML parser encounters a script it downloads and executes it before continuing to process the rest of the page.
defer attributes tell the browser that these scripts don’t have to run immediately and the browser can process the rest of the page first and then run these scripts later on.
What does the async attribute do?
An async-loaded script tag looks like this:
<script src="app.js" async>.
What does the defer attribute do?
defer attribute tells the browser to run the script after the document has been parsed. As with the
async attribute this ensures that the page can render in the meantime.
Once the document has been parsed the deferred scripts are run in the same order as they were found in the document. The
DOMContentLoaded event indicates that the document has been parsed and all deferred scripts have been run.
An deferred script tag looks like this:
<script src="app.js" defer>.
An example of the async and defer attributes in action
Let’s take a closer look at how
defer work in practice and what it means for page speed.
Rendering behavior without async and defer
() => console.log("DOMContentLoaded")
Top of page
Bottom of page
<script>console.log("End of Body")</script>
The request waterfall then shows how the page content appears gradually. At first no content is shown until after the first script has loaded. When that happens the page renders partially and the “top of page” text becomes visible.
The “bottom of page” text is placed below the second script, which means it only becomes visible after the second script has been downloaded and run by the browser. I’ve made
script2.js larger than
script1.js to make it take longer to download and demonstrate this gradual loading behavior.
In the console we can see that everything is executed in order:
End of Body
If the two scripts are render-blocking, why do they start downloading at the same time?
This is because browsers use a preload scanner in addition to the primary HTML parser. The preload scanner identifies upcoming resources ahead of time and starts downloading them early.
Without the preload scanner each file would have to finish loading before the next resource is discovered. Instead, browsers start downloading the following resources early, and when the HTML parser discovers them they often have already finished downloading and can be run right away.
Adding async and defer
Now let’s see what happens when we add the
defer attribute to the first script and
async to the second one.
<script src="/script1.js" defer></script>
<script src="/script2.js" async></script>
The two scripts now no longer block rendering, which means that all page content appears as soon as the document HTMl has been downloaded.
This is also reflected in the order of execution as we can see by the console output. We reach the “End of Body” inline script quickly and then the deferred script runs.
End of Body
The DOMContentLoaded event indicates that the document has been fully parsed and all deferred scripts have been run. It does not wait for the async script to finish downloading, and script 2 appears last in the console.
Switching async and defer
What if we switch around the async and defer attributes, and also change the order of script 1 and 2?
<script src="/script2.js" defer></script>
<script src="/script1.js" async></script>
This does not change anything visually in our example – neither script is render-blocking. And because of how the async and defer attributes work, script 1 still runs ahead of script 2, even though it’s positioned ahead of script 1 in the code.
End of Body
The async script (script1.js) runs as soon as it’s finished downloading. If we wanted to make script 1 and 2 run in order we could use the defer attribute for both.
The DOMContentLoaded event doesn’t fire until after the deferred script (script2.js) has run.
How to check if scripts are using defer and async
Run a speed test on your website to see whether scripts on your site block rendering. Open the Requests tab to see which scripts block rendering and which use the async or defer attribute.
You can also use Lighthouse-based tools like PageSpeed Insights to check which of your resources are render-blocking. Open the performance Opportunities section and look for the Eliminate render-blocking resources audit.
Both attributes make sure scripts don’t block rendering and help you optimize the First Contentful Paint metric. In most cases async is a good choice because it still loads and executes the code as soon as possible.
Use defer if you have multiple scripts that depend on each other.
defer also means that the code will run slightly later than with
async. That can be good if you want to leave the CPU main thread free for more important work. But it can also mean that functionality takes longer to become available to the user. If the user navigates away before the browser has finished parsing the document the script will not run at all, so you should prefer
async for analytics scripts.
How are async and defer different from placing scripts at the end of the body?
Placing scripts at the end of the body was common before the browsers supported the HTML
defer attributes. It ensures that the script doesn’t block the page from rendering.
If your HTML document is large then putting scripts at the end of the body element can mean it takes longer for the browser to discover the resource, as the full HTML resource needs to finish downloading first. In contrast, an
async script in the page
head would quickly be discovered by the preload scanner.
Browsers also can’t tell that a script at the end of the body element isn’t render-blocking, so they’ll assume it is and assign a high-priority to the request. This may help load your script more quickly, but could also increase bandwidth competition with render-blocking resources like CSS stylesheets. As a result these other resources may download more slowly.
Do the async and defer attributes impact SEO?
Google doesn’t look at the attributes directly, but using them can help you optimize page speed and Core Web Vitals. The Core Web Vitals became a ranking factor in 2021, so making your website load faster not only helps you deliver a better user experience but also helps you rank higher in Google.
You can use Google Search Console to see how well your website scores on the Core Web Vitals metrics.
Using async together with preload resource hints
async script script and a preload resource hint for the same file.
<link rel=”preload” href=”app.js” as=”script”>
<script src=”app.js” async>
The preload tag ensures that the application code is loaded with high priority. However, this can sometimes lower page speed when the app download competes with more render-blocking resources on the page. Run a speed test on your website to see whether important resources download more slowly due to bandwidth competition.
Async behavior when inserting script tags dynamically
Script tags that are created dynamically using
document.createElement and inserted using
appendChild or a similar function load asynchronously by default.
var script = document.createElement("script");
script.src = "https://code.jquery.com/jquery-3.7.0.js";
console.log(script.async); // outputs "true"
document.write in a blocking script tag will create a chain of render-blocking requests unless the
async attribute is added explicitly.
Monitor the page speed and Core Web Vitals of your website
Ensuring that requests don’t block rendering and load with the right priority is just one aspect of making sure your website delivers a good experience to users. DebugBear can provide a detailed performance analysis of your website and monitor site speed and Core Web Vitals over time.