Three settings in the optimisation plugin all sound alike: load JavaScript deferred, load it asynchronously, and delay JavaScript execution. Older reports from GTmetrix used to say “defer parsing of JavaScript”. PageSpeed Insights talks about render-blocking files. You switch one on, the score improves, and the image slider on the home page now sits frozen on its first slide.
The defer vs async JavaScript question is easier than the settings pages make it look. Both attributes tell the browser it can keep reading the page while a script downloads. They differ in when the script runs, and that one difference explains nearly every breakage. Delay-until-interaction, the third setting, is a different tool again, and it breaks things in a different way.
What a normal script does to the page
Without any attribute, a script referenced in the page works like a roadblock. The browser is reading the HTML from top to bottom. It reaches the script, stops, downloads the file, runs it, and only then continues reading. Everything after that point waits, including text the visitor could have been reading.
That behaviour exists for a reason. Old scripts were allowed to write directly into the page as it loaded, so the browser had to run them in place. Most modern scripts do not do that, which is why they can safely be told to step aside. Why this holds up the first paint, alongside stylesheets, is covered in the overview of render-blocking CSS and JavaScript.
Defer vs async JavaScript in plain terms
Think of the browser reading your page as a cook following a recipe, and scripts as ingredients being delivered.
With defer, the cook keeps working while deliveries arrive, and uses the ingredients in the order they were listed, once the recipe has been read through. With async, the cook keeps working too, but drops everything to use each ingredient the moment it arrives, in whatever order the van turns up.
| No attribute | defer | async | |
|---|---|---|---|
| Stops the browser reading HTML while downloading | Yes | No | No |
| When it runs | Immediately, in place | After the HTML has been read | As soon as it has downloaded |
| Runs in the order written | Yes | Yes | No |
| Works on inline code | Yes | No, ignored | No, ignored |
Deferred scripts still run before the browser fires its “DOM content loaded” event, so code waiting for that moment still works. Scripts loaded as JavaScript modules, marked with type="module", behave like deferred scripts by default.
When to use each
- defer suits most of your site’s own scripts: the theme’s main file, sliders, menus, form scripts. They usually depend on each other, and defer keeps them in order.
- async suits scripts that depend on nothing else and nothing depends on them. Analytics tags are the classic case, and Google’s tag snippets already load this way.
- Neither is right for the rare script that must run before the page is shown, such as a consent tool that has to block other tags until the visitor chooses, or code that sets up something the rest of the page needs immediately.
What breaks, and why
Nearly all breakage after deferring comes from one of these situations.
Inline code that expects jQuery
This is the big one on WordPress. A plugin or theme prints a short piece of code directly into the page, often something like “when the page is ready, start the slider”, and that code calls jQuery. Inline code cannot be deferred, so it runs straight away. If jQuery itself is now deferred, it has not arrived yet, and the inline code fails with an error like “jQuery is not defined”. The slider never starts.
The fixes, in order of preference: exclude jQuery from deferral; or use an optimisation plugin that can also hold back the inline scripts that depend on jQuery, which some can; or ask the plugin’s developer for a version that does not rely on inline code.
Order problems with async
Put async on a script that another script depends on, and sometimes the dependent arrives first and fails. Because download times vary, this can break on one visit and work on the next, which makes it maddening to diagnose. If a problem comes and goes when you refresh, suspect async.
Scripts that write into the page
Some old ad and widget code writes HTML into the page as it loads. Browsers ignore that kind of writing from scripts that do not run in place, so the widget quietly shows nothing.
Delay until interaction is a different tool
Many optimisation plugins now offer a third option, often called “delay JavaScript execution”. WP Rocket, Perfmatters, FlyingPress and the Flying Scripts plugin all have a version of it. Instead of downloading the script in the background, the plugin holds it back entirely until the visitor scrolls, taps, moves the mouse or presses a key.
For the right scripts this is excellent. A chat widget, a heatmap tool or a social feed at the bottom of the page does not need to load for someone who arrived and left. Chat tools in particular are covered in why a live chat widget slows a page.
But it has trade-offs that defer does not:
- Anything visible at the top breaks or waits. A slider, a mobile menu or a header animation that needs its script will not work until the first interaction.
- The first tap pays the bill. When the visitor finally interacts, all the delayed scripts load and run at once. On a slow phone that can make the first tap feel sluggish, which shows up in real-user measures of responsiveness.
- Analytics can miss visits. If you delay the analytics tag, visitors who leave without touching the page are never recorded. Most people keep analytics out of the delay list for that reason.
- Lab scores flatter it. A testing tool loads the page without interacting, so delayed scripts never run in the test. The score looks great; real visitors still download the scripts.
A sensible approach: defer your own scripts, and delay only third-party extras that have no visible job above the fold. If a script is not needed at all on a page, removing it beats both, which is the subject of cutting scripts a page never uses.
How WordPress fits in
Scripts on a WordPress site are added by themes and plugins through WordPress’s own loading system. Recent versions of WordPress let developers mark a script as deferred or async when they register it, and WordPress takes care of dependencies, so a well-written plugin can do this properly at the source.
Optimisation plugins work differently. They rewrite the finished page and add the attributes themselves, then let you exclude files that misbehave. That works on any site, but the plugin does not always know which files depend on which, so the exclusion list is where you spend your time.
A testing routine for script loading changes
- Change one setting. Clear the plugin, server and CDN caches.
- Open the page in a private window with the browser console open. Look for red errors, especially “is not defined”.
- Test without touching the page first: is anything at the top missing or frozen?
- Then interact: open the menu, use the slider, submit a form, add a product to the cart.
- Refresh several times. Problems that appear only sometimes point to async or delay ordering.
- Repeat on a phone over mobile data.
If a change goes badly, switching it off and clearing caches usually restores the site. The steps for working out which setting did the damage are in recovering when a caching plugin breaks your site. For how script loading compares with the other causes of a slow page, see our full guide to website speed.
Frequently asked questions
Is “defer parsing of JavaScript” the same as adding defer?
Broadly, yes. It was the wording older speed tools used for the advice to stop scripts holding up the page while it loads. Today the usual way to follow it is the defer attribute, or async for independent scripts, which most WordPress optimisation plugins can add for you with an exclusion list.
Should I defer jQuery?
Only if nothing on the page runs inline code that needs jQuery straight away, or if your plugin can defer that inline code too. On many WordPress sites it is simpler to exclude jQuery and defer the scripts that depend on it, then measure whether deferring jQuery as well adds anything worth the risk.
Can I use defer and async on the same script?
You can write both, but browsers that understand async will use async and ignore defer. The pairing was a fallback for very old browsers. In practice, choose one: defer for scripts that depend on others or that others depend on, and async for scripts that stand alone.
Should Google Tag Manager be deferred or delayed?
The standard Tag Manager snippet already loads its container asynchronously, so adding defer gains little. Delaying it until interaction can make pages look faster in tests, but visitors who leave without touching the page are then not recorded. The tags inside Tag Manager are usually the heavier part, so review those instead.
If script settings keep trading speed for broken features, our speed optimisation service includes JavaScript deferral with exclusions worked out for your theme and plugins, applied with your approval and checked on every interactive part of the page.
Next in this series: Critical CSS Explained: Faster First Paint, Fewer Surprises