Minify, Defer, Delay
This page is a practical strategy for controlling JavaScript on WordPress sites: reduce unnecessary bytes, move non-critical execution out of the critical rendering path, and keep the main thread calm for good INP. The hard part is avoiding conflicts (double-optimization) and keeping checkout/forms functional.
What Each Technique Does
| Technique | What It Does | Typical Benefit | Typical Risk |
|---|---|---|---|
| Minify | Shrinks JS transfer size | Small LCP improvement | Conflicts when multiple tools minify |
| Defer | Runs scripts after HTML parsing | Better LCP/INP, fewer long tasks early | Breakage if a script must run before paint |
| Delay | Prevents execution until interaction/timeout | Big INP wins for third-party scripts | Breakage if critical scripts are delayed |
Avoid Conflicts (One Tool Owns JS)
Choose one tool to handle JS optimization. If Perfmatters is your primary JS tool:
- LSCache: disable JS minify/defer/delay features.
- Cloudflare: disable Rocket Loader and JS Auto Minify.
- Autoptimize/other optimizers: disable JS optimization features.
Conflicts are the fastest way to ship broken pages. If something breaks after enabling minify/defer/delay, the first step is to disable overlapping JS optimization in other tools.
Perfmatters Baseline (Recommended)
The exact labels vary by version, but the strategy is consistent:
- Minify JS: ON
- Defer JS: ON
- Delay JS: ON
- Delay timeout: set a fallback (so delayed scripts still run eventually)
Static vs Dynamic Starting Points
Use these as initial defaults and refine based on real templates.
Static sites (blogs/docs/marketing) - starter exclusions
Static sites can often delay most third-party scripts.
jquery
recaptcha
grecaptcha
Dynamic sites (WooCommerce/LMS/memberships) - starter exclusions
Dynamic sites need tighter exclusions for cart/checkout/forms/payment flows.
jquery
wc-add-to-cart
wc-cart
wc-checkout
wc-cart-fragments
recaptcha
grecaptcha
stripe
elements.js
paypal
braintree
klarna
How to Build a Good Exclusion List
- Start minimal.
- Test one template at a time (home, post, product, cart, checkout).
- When something breaks, exclude the script handle/keyword responsible.
- Keep notes (why an exclusion exists) so you can revisit it later.
Common categories that often need exclusions:
- Payments (Stripe/PayPal)
- Forms + captcha
- Cart/checkout fragments
- Theme navigation scripts
- Video players you expect to work immediately
Testing Workflow
- Test logged-out in an incognito window.
- Use DevTools Console: errors like
$ is not definedusually indicate a dependency was delayed. - Perform real interactions: open mobile menu, add to cart, submit form, checkout.
- Re-run Lighthouse/PSI and compare:
- INP and long tasks
- total JS bytes
- main thread time
Common Breakages and Fixes
| Symptom | Likely Cause | Fix |
|---|---|---|
| Menu/accordion is dead | A theme script was delayed | Exclude that script from Delay |
| Add-to-cart fails | Woo scripts delayed | Exclude wc-add-to-cart and related handles |
| Checkout validation fails | Gateway SDK delayed | Exclude gateway keywords (stripe/paypal/etc.) |
| Forms don't submit | Form/captcha scripts delayed | Exclude form plugin + recaptcha/grecaptcha |
| Analytics misses first pageview | Analytics delayed too long | Use a shorter timeout or do not delay analytics |
Optional: Code-Based Defer (Fallback)
If you cannot use Perfmatters in a specific environment, you can add defer to many enqueued scripts.
This is a blunt tool. It can break themes/plugins. Prefer a dedicated optimization plugin and test on staging.
add_filter('script_loader_tag', function($tag, $handle, $src) {
$exclude = [
'jquery-core',
'jquery-migrate',
'wc-add-to-cart',
];
if (in_array($handle, $exclude, true)) {
return $tag;
}
if (str_contains($tag, ' defer')) {
return $tag;
}
return str_replace(' src', ' defer src', $tag);
}, 10, 3);