Adding defer to your scripts is one of the cheapest speed wins on a WordPress site. It is also the setting most likely to break a menu, a slider or a checkout field. This post explains why, with a small example, and shows a safe way to roll it out.

Disclosure: I'm part of the team behind NodeThemes Cache AI, a free WordPress cache plugin that tests changes like this one. The technique below works with or without it.

What defer actually changes

A classic <script src> in the <head> blocks parsing: the browser downloads and runs it before it builds the rest of the page. With defer, the browser keeps parsing and runs the script later. According to MDN, deferred scripts:

  • run after the document has been parsed, before DOMContentLoaded
  • run in the order they appear
  • only apply to external scripts (defer on an inline script does nothing)
Attribute Blocks parsing? When it runs Order kept?
none Yes Immediately Yes
defer No After parsing Yes
async No As soon as it downloads No

The WordPress-specific problem: inline scripts

WordPress themes and plugins love to print a small inline script right after a file. wp_add_inline_script() does exactly that, and many themes echo their own:

<script src="/wp-includes/js/jquery/jquery.min.js"></script> <script> jQuery(function ($) { $('.hero-slider').slick(); }); </script> 

Now defer the file:

<script src="/wp-includes/js/jquery/jquery.min.js" defer></script> <script> jQuery(function ($) { $('.hero-slider').slick(); }); // runs NOW </script> 

The inline block is not deferred, so it runs while jQuery hasn't loaded yet:

Uncaught ReferenceError: jQuery is not defined 

The slider stays empty, and unless someone opens the console, nobody knows why. The same thing happens with … is not a function when a plugin's script is deferred and another script calls it early.

A safe rollout, step by step

  1. Defer everything except jQuery. Keep jquery.min.js and jquery-migrate loading normally; they are the most common dependency of inline code.
  2. Test the pages that matter: home, a post, a page, the shop and a product. Open menus, tabs and sliders, not just the first screen.
  3. Read the console. The error names the file and line. Add that file to a "do not defer" list.
  4. Repeat until there are no new errors, then ship.
  5. Retest after theme and plugin updates. An update can add new inline code.

A quick way to spot new errors is to compare the console before and after the change. Errors that were already there without defer are not your problem today; new ones are.

Automating the comparison

Doing this by hand means a lot of reloading with DevTools open, so we automated it in the free NodeThemes Cache AI plugin. It opens each test page in hidden frames in the admin's own browser: twice without the new settings and once with them. Then it compares:

  • JavaScript errors (new ones only, so existing errors on the site don't fail the test)
  • files and images that fail to load
  • the size of the main page parts and the page height

Settings that fail are not applied, and every change can be rolled back with one click. The Pro add-on goes a step further when a test fails: it splits the deferred scripts in halves and retests until it finds the few files that must not be deferred, and keeps everything else deferred.

Does it help Core Web Vitals?

Often, yes, mainly LCP, because the page can render before the scripts run. Measure on mobile, compare like with like, and confirm with real-visitor data over the following days.

The full guide, with a FAQ and more WordPress specifics, is here: Defer JavaScript in WordPress without breaking your site.