WordPress Speed

How to Defer JavaScript in WordPress (Without Breaking Your Site)

Render-blocking JavaScript is killing your PageSpeed score. Here's how to defer it properly in WordPress — and which scripts you must not touch.

Jamie McKaye
Jamie McKaye
10 min read
How to Defer JavaScript in WordPress (Without Breaking Your Site)
Key Takeaways
  • Render-blocking JavaScript prevents the browser from painting anything on screen until scripts are downloaded and executed — this directly inflates FCP and LCP
  • The defer attribute downloads scripts in parallel but delays execution until HTML parsing is complete — it’s the correct choice for most WordPress scripts
  • async downloads in parallel but executes immediately when ready, which can cause race conditions — use it only for independent scripts like analytics
  • jQuery is the single biggest render-blocking offender on most WordPress sites, and removing it from the frontend is the highest-impact JavaScript optimisation
  • Never defer scripts that control above-fold interactivity or visual layout — test thoroughly on staging before deploying any defer changes

What Render-Blocking JavaScript Actually Does

When a browser encounters a <script> tag in your HTML without async or defer attributes, it stops everything. It pauses HTML parsing, downloads the script file, executes it, and only then resumes building the page. If your site loads ten JavaScript files in the <head>, the browser must download and execute all ten before it paints a single pixel.

This is why you see the “Eliminate render-blocking resources” warning in PageSpeed Insights. Every render-blocking script adds latency between the user requesting your page and seeing anything on screen. On a typical WordPress site with 8–15 JavaScript files loading in the head, render-blocking JS adds 1–3 seconds to First Contentful Paint on mobile connections.

The solution isn’t removing JavaScript — it’s changing when it loads. By adding defer or async attributes, you tell the browser to download scripts without pausing HTML parsing. The page renders immediately while scripts load in the background.

The Difference Between async and defer

Both attributes allow the browser to download scripts without blocking HTML parsing. The critical difference is when the script executes.

Comparison of normal script loading blocking the parser, async loading in parallel with a small interruption, and defer loading in parallel with execution after parsing completes
Normal loading blocks HTML parsing entirely. Async downloads in parallel but still interrupts. Defer downloads in parallel and executes only after parsing completes — the safest option for most scripts.

defer: The script downloads in parallel with HTML parsing. Execution is delayed until the HTML document is fully parsed. Multiple deferred scripts execute in the order they appear in the HTML. This preserves dependency order — if script B depends on script A, and both are deferred, script A will always execute first.

async: The script downloads in parallel with HTML parsing. Execution happens immediately when the download completes — regardless of whether the HTML is fully parsed or other scripts have finished. Execution order is unpredictable. If script B depends on script A, but script B’s file is smaller and downloads first, script B executes first and throws errors because script A hasn’t run yet.

When to use defer: Most WordPress scripts. Theme JavaScript, plugin scripts, jQuery-dependent code, and any script that interacts with the DOM. Defer preserves execution order and waits for the DOM to be ready — it’s the safe default choice.

When to use async: Independent scripts that don’t depend on other scripts and don’t interact with the DOM during initial load. Google Analytics, Facebook Pixel, and similar tracking scripts are ideal candidates for async.

When to use neither: Scripts that must execute immediately during parsing — typically only critical above-fold interaction scripts or polyfills that other scripts depend on. In practice, very few WordPress scripts genuinely need synchronous loading.

How to Identify Render-Blocking Scripts on Your Site

Before deferring anything, identify exactly which scripts are blocking rendering and how much time each one costs.

PageSpeed Insights: Run your URL through PageSpeed Insights (see our complete testing guide for how to interpret the results). The “Eliminate render-blocking resources” diagnostic lists every CSS and JavaScript file that blocks the first paint. It shows the file URL, size, and estimated time savings from deferring each one.

Chrome DevTools Performance tab: Open DevTools, go to the Performance tab, click the reload button to record a page load. In the resulting waterfall, look for long yellow blocks (JavaScript execution) that appear before the first green paint event. These are your render-blocking scripts. The wider the block, the more time that script costs.

Chrome DevTools Network tab: Filter by JS, sort by “Initiator”, and look for scripts loaded in the <head>. Any script without defer or async in its HTML tag is render-blocking. Right-click a script and select “Block request URL” to simulate removing it — reload and see how much faster the page renders without it.

Common offenders on WordPress sites: jQuery (jquery.js and jquery-migrate.js), theme bundle files (theme.js, main.js), plugin scripts loaded globally (contact form JS on non-form pages, slider JS on pages without sliders), and Google Fonts loader (webfont.js).

Method 1: The functions.php Approach

The most direct method is adding defer attributes to script tags using WordPress’s script_loader_tag filter. This works at the theme level and gives you granular control over which scripts get deferred.

// Add defer to all enqueued scripts except specified exclusions
add_filter('script_loader_tag', function($tag, $handle, $src) {
    // Scripts to NOT defer (above-fold critical scripts)
    $exclude = ['critical-above-fold'];
    
    if (in_array($handle, $exclude)) {
        return $tag;
    }
    
    // Don't add defer to scripts that already have it or async
    if (strpos($tag, 'defer') !== false || strpos($tag, 'async') !== false) {
        return $tag;
    }
    
    return str_replace(' src=', ' defer src=', $tag);
}, 10, 3);

Pros: Full control. No plugin dependency. Minimal code. You choose exactly which scripts are excluded. Works with any hosting setup.

Cons: Requires editing theme files (use a child theme or custom plugin to survive theme updates). Requires testing to build the exclusion list. Doesn’t handle inline scripts or scripts added outside WordPress’s enqueue system.

Best practice: Start by deferring everything, then add exclusions as you discover breakage during testing. The exclusion list is usually very short — most WordPress scripts work fine when deferred.

Method 2: WP Rocket’s Delay JavaScript Feature

WP Rocket goes beyond simple deferral with its “Delay JavaScript execution” feature. Instead of loading scripts when the page loads, it delays loading entirely until the user interacts with the page (mouse movement, scroll, keypress, touch).

How it works: WP Rocket removes all JavaScript from the initial page load. When the browser detects user interaction, it injects the scripts and executes them. This means the initial page render has zero JavaScript overhead — First Contentful Paint and Largest Contentful Paint see dramatic improvements.

Pros: Maximum FCP/LCP improvement. No code to write. Built-in safelist for common plugins. Easy to configure via the WP Rocket dashboard.

Cons: Interactive elements don’t work until the user triggers script loading. If a user tries to click a button or submit a form before interacting elsewhere on the page, nothing happens until scripts load. This can feel broken. The delay is typically 100–300ms after first interaction, but it’s noticeable on complex sites. WP Rocket costs $59/year for a single site.

When to use it: Content-heavy sites where the primary above-fold content is text and images (blogs, news sites, informational sites). Not ideal for sites where the first thing users do is interact with a form, calculator, or interactive widget above the fold.

Method 3: Autoptimize (Aggregate + Defer)

Autoptimize combines multiple JavaScript files into a single file (aggregation) and defers the combined file. This reduces both the number of HTTP requests and render-blocking behaviour.

Pros: Free. Handles CSS optimisation too. Reduces HTTP request count. The aggregated file can be cached efficiently. Good for sites on HTTP/1.1 (where reducing requests matters more than on HTTP/2).

Cons: Aggregation can break scripts that depend on load order. The aggregated file is large (all scripts combined), so if it fails to load, the entire site’s JavaScript breaks. On HTTP/2 and HTTP/3, aggregation provides minimal benefit because the protocol handles multiple files efficiently. Cache invalidation requires regenerating the entire aggregated file when any individual script changes.

When to use it: Budget-conscious sites that need a free solution. Sites still on HTTP/1.1 where reducing request count provides measurable benefit. Always test the “Also aggregate JS files” option carefully — it’s the most common source of breakage.

Which Scripts You Must NEVER Defer

Some scripts must execute during page parsing to avoid visual or functional breakage:

Above-fold interaction scripts: If your hero section contains a form, calculator, or interactive element that users engage with immediately, the JavaScript powering it should not be deferred. JavaScript execution directly affects your Interaction to Next Paint (INP) Core Web Vital. Users expect instant interactivity for visible elements.

Layout-dependent scripts: Scripts that calculate element heights, set scroll positions, or adjust layout based on viewport size can cause layout shift (CLS) if deferred. The page renders without the script’s adjustments, then “jumps” when the script executes.

Critical polyfills: If you’re using polyfills for browser compatibility (IntersectionObserver, ResizeObserver), these must be available before the scripts that depend on them. Load polyfills synchronously or with defer (which preserves order), never with async.

Anti-flicker scripts: If you’re running A/B testing (Google Optimize, VWO), the anti-flicker snippet must execute synchronously to prevent the original page content from flashing before the variant loads.

The jQuery Problem

jQuery is loaded on nearly every WordPress site. It’s listed as a dependency by hundreds of plugins and many themes. WordPress core itself enqueues jQuery for admin functionality.

The problem: jQuery is render-blocking by default, typically 90KB+ (with jquery-migrate), and loads on every page even if the frontend doesn’t use it. It’s often the single largest render-blocking resource on WordPress sites.

jQuery dependency web showing a central jQuery node with dozens of plugin dependencies radiating outward in a tangled network
jQuery sits at the centre of a dependency web — dozens of plugins depend on it, making it nearly impossible to defer without careful testing.

Can you defer jQuery? Sometimes. If every script that depends on jQuery uses proper jQuery(document).ready() or $(function() {}) wrappers, deferring works because the DOM is parsed before deferred scripts execute. But many plugins use inline scripts that call $(...) immediately — these break when jQuery is deferred because jQuery hasn’t loaded yet when the inline script runs.

Can you remove jQuery from the frontend? This is the ideal solution. Modern JavaScript and CSS handle everything jQuery does. If your theme doesn’t depend on jQuery and you can identify which plugins need it:

// Deregister jQuery on the frontend only (keep it in admin)
add_action('wp_enqueue_scripts', function() {
    if (!is_admin()) {
        wp_deregister_script('jquery');
        wp_deregister_script('jquery-migrate');
    }
});

Warning: This will break any plugin or theme feature that depends on jQuery on the frontend. Test thoroughly. Common breakage: contact forms (WPForms, Gravity Forms), sliders, lightboxes, mega menus, and WooCommerce. If more than 2–3 features break, it’s usually not worth removing jQuery — defer it instead and accept the small performance cost.

Testing After Deferring Scripts

Deferring JavaScript is one of the most common causes of “it works on desktop but breaks on mobile” issues. Here’s a thorough testing protocol:

Console errors: Open Chrome DevTools Console. Reload the page. Look for any new errors — particularly $ is not defined (jQuery loaded too late), Cannot read property of undefined (dependency order broken), or ReferenceError (script tried to use a function from another script that hasn’t executed yet).

Interactive element testing: Click every button, open every dropdown, submit every form, trigger every modal, expand every accordion, and test every slider. Do this on both desktop and mobile viewports. Deferred script breakage often only manifests on interaction.

Scroll-based features: Test lazy loading, scroll animations, sticky elements, and infinite scroll. These features rely on scroll event listeners that may not be registered if their scripts are deferred incorrectly.

Third-party integrations: Test payment gateways (Stripe, PayPal), live chat widgets, cookie consent banners, and analytics tracking. These often load via inline scripts that assume their parent library is already available.

WooCommerce specifics: Add to cart, update cart quantities, apply coupons, proceed through checkout, and complete a test payment. WooCommerce’s cart fragments AJAX, variation selectors, and checkout validation are all jQuery-dependent and frequently break when jQuery is deferred.

The Advanced Move: Going jQuery-Free

For sites where performance is the top priority, eliminating jQuery entirely from the frontend is the single most impactful JavaScript optimisation. jQuery was essential in 2010 when browsers were inconsistent. In 2026, native JavaScript APIs handle everything jQuery does — and faster.

The modern replacements: document.querySelector() replaces $(). element.addEventListener() replaces .on(). fetch() replaces $.ajax(). element.classList replaces .addClass() / .removeClass(). CSS transitions and the Web Animations API replace .animate().

The VeloPress site you’re reading uses zero jQuery. Every interaction — the mobile menu, scroll animations, the speed calculator, form submissions — runs on vanilla JavaScript. The result: zero render-blocking JavaScript dependencies, faster execution, and a smaller payload. This is exactly the approach we take for client sites where we’re building or rebuilding the theme.

For existing sites with deep jQuery dependencies, the pragmatic approach is: defer jQuery (accepting some risk), remove unused plugin scripts via Asset CleanUp, and focus your effort on the other five layers of the optimisation stack where gains are easier to achieve.

Frequently Asked Questions

Will deferring JavaScript break my WordPress site?

It can. The most common breakage is jQuery-dependent features (forms, sliders, menus) failing because jQuery loads after inline scripts that need it. Always test on staging first. Start by deferring everything, then add exclusions for scripts that cause visible breakage. Most sites need only 1–3 exclusions.

Should I use async or defer for WordPress scripts?

Use defer for most scripts — it preserves execution order and waits for HTML parsing to complete. Use async only for independent third-party scripts like Google Analytics or Facebook Pixel that don’t depend on other scripts or DOM elements. Never use async for jQuery or jQuery-dependent scripts.

Is WP Rocket’s delay JavaScript worth the cost?

For content sites (blogs, news, informational pages), yes — the FCP improvement is significant. For interactive sites (SaaS dashboards, web apps, sites with above-fold forms or calculators), the interaction delay can frustrate users. The free functions.php approach with defer gives 80% of the benefit with zero cost.

How much does deferring JavaScript improve PageSpeed scores?

Typically 10–25 points on mobile, depending on how many render-blocking scripts your site loads. Sites with 10+ render-blocking scripts in the head see the biggest improvements. If your only render-blocking script is jQuery (90KB), expect a 5–10 point improvement. Combined with removing unused CSS, you can often gain 30+ points.

Can I remove jQuery from WordPress entirely?

From the frontend, yes — if your theme and plugins don’t depend on it for visitor-facing features. WordPress admin always needs jQuery, so only deregister it on the frontend. Test every interactive element thoroughly. If more than 2–3 features break, it’s usually easier to defer jQuery rather than rewriting all the dependent code.

JavaScript still blocking your renders?

VeloPress eliminates render-blocking resources on every project

We defer, delay, and where possible remove JavaScript dependencies entirely. The result: zero render-blocking scripts and sub-second First Contentful Paint.

Get Your Free Audit →
View Pricing
Jamie McKaye

Jamie McKaye

Founder, VeloPress

Founder of VeloPress. 18 years in digital marketing, SEO, and web performance engineering. Optimised 150+ WordPress sites to score 95-100 on PageSpeed Insights. Based in Surrey, UK.

Get Started

Still struggling with WordPress speed?

VeloPress fixes this every day. We'll diagnose exactly what's slowing your site down and build a plan to hit 90+ on PageSpeed Insights.

Get Your Free Audit View Pricing

See what a full audit looks like — view sample report