WordPress JavaScript Errors: Fixing Plugin and Theme Issues
A typical WordPress site loads scripts from the theme, from each plugin and from WordPress itself, and none of them was written with the others in mind. That is why a slider stops working after a plugin update, a contact form fails only on one page, or a performance plugin setting breaks the page builder.
This guide shows how to find which plugin or theme owns each script and error, fix the jQuery mistakes that cause most WordPress conflicts, stop plugins loading their files on every page, and defer what remains using WordPress's own loading options.
How WordPress loads JavaScript
Well-behaved themes and plugins do not print script tags directly. They register each file with wp_enqueue_script(),
giving it a handle (a short name), an address, a list of other handles it depends on, a version and loading
options. WordPress then prints the tags in the right order, once each, in the head or the footer. That
system is what makes it possible to reorder, remove or defer scripts safely, because you can target a
script by its handle.
Problems start when code sidesteps it:
- Hard-coded script tags in
header.php,footer.phpor a "header and footer scripts" box cannot be dequeued, ordered or deferred by WordPress. - Plugins that bundle their own copy of a library, such as jQuery or a slider, can load it twice or overwrite the version other plugins expect.
- Missing dependencies. A plugin that uses jQuery but does not list
jqueryas a dependency may run before jQuery exists, which works by luck until something changes the order.
Since WordPress 6.3, wp_enqueue_script() accepts a strategy of defer
or async. WordPress checks the dependency tree and only applies the strategy where it is
safe, which makes it the cleanest way to defer scripts on a WordPress site.
Seeing who loads what
The JavaScript Checker loads the page in headless Chromium and lists each script with its size and execution time, the console errors and the render-blocking files. That tells you what the visitor's browser has to deal with. Two free plugins then connect each file to its source inside WordPress:
| Tool | What it shows | How to use it here |
|---|---|---|
| Query Monitor | Every enqueued script on the current page with its handle, address, dependencies, dependents and the plugin or theme that registered it. | Match each file from the checker to a handle and an owner. Missing dependencies are highlighted. |
| Health Check & Troubleshooting | A troubleshooting mode that disables plugins and switches theme for your logged-in session only. | Isolate a conflict on the live site without visitors noticing. |
| Browser DevTools | The console error, the file and line, and the call stack. | Folders
such as /wp-content/plugins/plugin-name/ in the address name the owner directly. |
To make errors in core WordPress scripts readable, add define( 'SCRIPT_DEBUG', true );
to wp-config.php on staging. WordPress then loads the unminified versions of its own files,
so line numbers point at readable code. Switch it off again afterwards.
The jQuery problems behind most WordPress conflicts
WordPress still ships jQuery, and many themes and plugins use it. Three mistakes account for a large share of WordPress console errors:
1. Using $ without wrapping the code
WordPress loads jQuery in no-conflict mode, so the global $ shortcut is not set. Code
copied from a tutorial that starts with $(document).ready fails with $ is not a function
or $ is not defined. Pass $ in instead:
jQuery(function ($) {
$('.faq-toggle').on('click', function () {
$(this).next('.faq-answer').slideToggle(150);
});
});
2. Relying on removed jQuery functions
WordPress moved to a modern jQuery version several years ago. Old plugins and themes that call long-removed
methods such as .live(), .size() or .andSelf() fail with messages
like jQuery(...).live is not a function. Update the plugin or theme, or replace the call
(.on(), .length, .addBack()) in your own code. Relying on jQuery
Migrate to hide such errors only postpones the fix.
3. Inline scripts that run before jQuery loads
If jQuery is moved to the footer or deferred, any inline script in the page body that calls it will
fail. Attach the code to the right handle with wp_add_inline_script() so WordPress prints
it after the file it needs:
wp_add_inline_script( 'example-booking', 'window.exampleBooking.init();', 'after' );
querySelectorAll, classList and addEventListener
do the same job and remove a dependency.The fix, step by step
Step 1
List the scripts on each template
Run the JavaScript Checker on your home page, a post, a page built with your page builder, a shop or archive page and the contact page, and save the scripts, sizes and console errors for each. Then install Query Monitor on staging, visit the same pages and open its Scripts panel. Write down the handle and owning plugin or theme for every file, and note any file that loads on pages where it does nothing.
Step 2
Trace each error to a plugin or theme
Open the browser console, reload, and click the file name beside each error. The address
shows the owner: /wp-content/plugins/ followed by the plugin folder, /wp-content/themes/
for the theme, or /wp-includes/ for WordPress itself. Expand the stack trace, because an
error thrown inside jQuery or core is often caused by the plugin that called it, which appears lower
down the stack.
Step 3
Isolate conflicts in troubleshooting mode
With the Health Check & Troubleshooting plugin, enter troubleshooting mode, which disables all plugins and switches to a default theme for your session only. Enable your theme and the plugin that owns the error, then add the other plugins back one at a time, reloading with the console open each time. When the error returns, the last plugin enabled is the other half of the conflict. Report both to the developers with the exact message.
Step 4
Fix jQuery assumptions in custom code
Search your child theme and any code snippets plugin for $( used outside a wrapper
and wrap that code in jQuery(function ($) { ... }). Replace removed methods such as .live()
and .size(). Make sure every script you enqueue lists jquery as a dependency
if it uses it, and move inline code that calls a library into wp_add_inline_script() so
it always prints after that library.
Step 5
Load plugin scripts only where they are used
For each plugin whose files load everywhere but are needed on a few pages, first
check its settings for an option to load assets only where its shortcode or block appears. If there is
none, dequeue its handle with wp_dequeue_script() in a small function hooked to wp_enqueue_scripts
at a late priority, wrapped in a condition such as ! is_page( 'contact' ) so it only stops
on pages that do not need it. Test the pages where it is still needed.
Step 6
Defer scripts with the WordPress loading strategy
For scripts your theme or custom plugin enqueues, pass an arguments array with
'strategy' => 'defer' and 'in_footer' => true to wp_enqueue_script().
WordPress checks dependencies and keeps a script blocking if something that depends on it cannot wait.
Avoid rewriting script tags for plugins with the script_loader_tag filter unless you have
tested every page that plugin affects, because it bypasses those checks.
Step 7
Set optimisation plugin exclusions and retest
If you use a performance plugin with options to defer, combine or delay JavaScript, turn them on one at a time on staging. After each, test menus, sliders, forms, the basket and the page builder editor. Add the handles or file names of anything that breaks to the plugin's exclusion list. Then re-run the JavaScript Checker on the same templates as step 1 and compare errors, blocking files and execution time.
Example: enqueueing and trimming scripts correctly
This goes in a child theme's functions.php or a small site-specific plugin. The handles
example-gallery and example-forms are placeholders; take the real handles from
Query Monitor.
add_action( 'wp_enqueue_scripts', function () {
// Our own script: deferred, in the footer, loaded only on single posts.
if ( is_singular( 'post' ) ) {
wp_enqueue_script(
'example-gallery',
get_stylesheet_directory_uri() . '/js/gallery.js',
array(),
'1.4.0',
array( 'strategy' => 'defer', 'in_footer' => true )
);
}
}, 20 );
add_action( 'wp_enqueue_scripts', function () {
// A form plugin's script is only needed on the contact page.
if ( ! is_page( 'contact' ) ) {
wp_dequeue_script( 'example-forms' );
}
}, 100 );
Page builders, block themes and optimisation plugins
- Page builders. Elementor, Divi, Beaver Builder and similar tools load scripts for animations, sliders and popups that must run early. "Delay JavaScript until interaction" settings often break them, and some break only in the editor. Use the exclusion lists your optimisation plugin provides, and switch off builder widgets you do not use in the builder's own settings.
- Combining files. Merging all scripts into one file was useful before HTTP/2. Today it often causes errors by changing load order and makes caching worse, since any change invalidates the whole file. Turn combining off unless testing shows a clear gain.
- Block themes. Block themes can load the styles and scripts for core blocks only on pages that use them. Third-party block plugins vary, so check whether their files appear on pages without their blocks.
- Newer WordPress APIs. Recent releases add a script modules API and the Interactivity
API used by some core blocks. If you see
type="module"scripts from/wp-includes/, they are part of WordPress and already load without blocking. - WooCommerce. Cart fragment scripts and checkout scripts are needed on shop pages. Test the basket, mini-cart and checkout after any change that touches them.
Keeping WordPress scripts under control
- Update on staging first, then run the JavaScript Checker on your key templates before updating live.
- Remove plugins you do not use, rather than just deactivating and forgetting them.
- Keep custom code in a child theme or site plugin, enqueued properly, never pasted into header boxes.
- Record your dequeue rules and optimisation exclusions in one place with the reason for each.
- Watch real-user errors. A single test load misses errors that only happen for logged-in users, specific browsers or after a form submission.
For loading speed and layout shift on WordPress, see the WordPress Core Web Vitals guide.
1. Why do I get "$ is not a function" on my WordPress site?
WordPress loads jQuery in no-conflict mode, which means the dollar sign shortcut is not defined globally. Code that starts with $( outside a wrapper fails with this error. Wrap it as jQuery(function ($) { your code }) so the dollar sign is passed in and works inside. The error can also appear when a script runs before jQuery has loaded, so check that the script lists jquery as a dependency when it is enqueued.
2. How do I find which plugin is causing a JavaScript error?
Open the browser console and click the file name next to the error. If the address contains wp-content/plugins followed by a folder name, that folder is the plugin. If the error is inside jQuery or a core file, expand the stack trace and look for the plugin further down. For conflicts between two plugins, use the Health Check and Troubleshooting plugin to disable everything for your session and re-enable plugins one at a time.
3. Can I defer jQuery in WordPress?
Sometimes, but it is risky. Many plugins print inline scripts in the page that call jQuery immediately, and those fail if jQuery has not loaded yet. WordPress's own loading strategy will not defer a script when something depending on it needs to run straight away. If you want to try, do it on staging with your optimisation plugin, check every page type, and exclude any inline code that breaks rather than forcing it on the whole site.
4. Is it safe to use "delay JavaScript execution" in a caching plugin?
It can be, with care. Delaying scripts until the first scroll, tap or key press reduces work during load, but anything that must run on arrival, such as sliders, cookie banners, above-the-fold animations and page builder features, may appear broken until the visitor interacts. Enable the option on staging, go through every template, and add broken scripts to the exclusion list. Check that the first interaction does not become slow as all the delayed scripts run together.
5. How do I stop a plugin loading its JavaScript on every page?
First check the plugin's settings, because many form, slider and gallery plugins offer an option to load their files only where they are used. If not, find the script handle in Query Monitor and call wp_dequeue_script with that handle in a function hooked to wp_enqueue_scripts at a late priority, wrapped in a condition for the pages that do not need it. Test the pages that still use the plugin afterwards to make sure nothing was lost.
6. What does the WordPress script loading strategy do?
Since WordPress 6.3, wp_enqueue_script accepts an arguments array with a strategy of defer or async and an in_footer setting. WordPress examines the dependency tree before printing the tag. If another script that depends on yours is blocking, or an inline script attached before it would run too early, WordPress can fall back to a safer loading method. That makes it more reliable than adding defer to tags with a filter or a regular expression.
7. Why did JavaScript break after a WordPress or plugin update?
Updates change load order, library versions and the elements scripts expect to find. A theme may rely on a plugin script that was renamed, a plugin may call a jQuery method that no longer exists, or an optimisation plugin may have cached old combined files. Clear every cache, check the console for the first error, trace it to its owner, and roll back that one update on staging if needed while you wait for a fix from the developer.
The JavaScript Checker loads your page in headless Chromium, captures console errors and warnings, lists every theme and plugin script with its size and execution time, flags render-blocking files and shows the cost of third-party tags.
Open JavaScript Checker Fixes โ