JavaScript “$ is not a function” means $ isn’t a callable jQuery function on that page—load jQuery correctly and avoid $ conflicts.
You click a button, your page freezes, and the console throws the same line again. This error shows up often in old sites, new builds, WordPress themes, Shopify snippets, and single-page apps. The good news is that the message is literal. Your code tried to run $() like a function, and at that moment $ was something else.
This article helps you spot what $ is right now, why it changed, and what to fix in a repeatable way. You’ll also get copy-ready checks for common setups: script tags, bundlers, WordPress, and “noConflict” pages.
What The Error Means In Plain Terms
In JavaScript, a “function” is a value you can call with parentheses, like fn(). When you see “$ is not a function,” the browser is saying that the current value stored in the variable named $ can’t be called.
Most of the time, people expect $ to be jQuery’s function. jQuery sets both window.jQuery and window.$. If jQuery didn’t load, loaded too late, or got replaced, then $ won’t be the thing you expect.
Quick check — open DevTools, go to the Console, and type typeof $. If you get "function", jQuery is present and callable. If you get "undefined", jQuery isn’t on the page at all. If you get "object" or "string", something overwrote it.
Fast Diagnosis Steps You Can Do In Two Minutes
Before changing code, pin down the root cause. A small check now saves you from random edits later.
- Reproduce the error — Trigger the click, load, or timer that throws it, then note the first file and line number in the stack trace.
- Check jQuery presence — Run
window.jQueryandwindow.jQuery && typeof window.jQueryto see if it exists and what type it is. - Inspect the $ value — Run
$andtypeof $. If it prints something like a DOM element or a library object, you’ve found the collision. - Verify script order — In the Elements panel, find your script tags and confirm jQuery loads before the script that calls
$(). - Look for duplicate loads — In the Network tab, filter by “jquery” and check if you load it more than once, from different versions.
When you have those answers, the fix almost always falls into one of the patterns below.
Common Causes And Fixes That Work
| What You See | Likely Cause | Fix To Try |
|---|---|---|
| $ is undefined | jQuery not loaded | Load jQuery before your code |
| $ is an object | Another library took $ | Use jQuery or noConflict |
| Error only on some pages | Scripts conditionally enqueued | Load dependencies on every page that needs them |
| Works after refresh, fails on first load | Code runs before DOM or before scripts finish | Wrap in DOM-ready or defer correctly |
| Fails after a plugin update | Version mismatch or duplicate jQuery | Remove extra copies; keep one source |
Load jQuery before the code that uses $
If you use plain script tags, order is the first thing to fix. jQuery must load first, then your plugin, then your custom script. If you use defer or async, be careful: async can reorder loads, and that can break pages.
- Move jQuery earlier — Place the jQuery script tag above your script that calls
$(). - Avoid async on jQuery — Keep jQuery as a normal script or use
deferon all related scripts so order stays consistent. - Confirm the URL loads — Open the jQuery URL in a new tab and check for 404s, blocked requests, or CSP errors.
Also check caching — If you use a cache plugin or CDN, an old HTML version can point at a removed file. Purge cache, hard-reload, then recheck Network for the right jQuery file. If your site uses a Content Security Policy, confirm the jQuery host is allowed, or the browser will block it silently. In the Console, CSP blocks show as red errors with “Refused to load script”. Fix the policy, then reload. Test in incognito once too.
Stop $ collisions with noConflict or by using jQuery
Many libraries like Prototype, MooTools, Zepto, and some theme scripts can use $ too. If that library loads after jQuery, it can replace window.$. jQuery still exists as window.jQuery, so the simplest fix is to call jQuery() instead of $().
- Swap $ for jQuery — Change
$(selector)tojQuery(selector)in your code. - Wrap your code — Use
(function($){ ... })(jQuery);so $ is locally mapped to jQuery inside that block. - Use noConflict — Call
jQuery.noConflict()only when you truly need $ for another library.
Make sure your code runs after the page is ready
If jQuery is present but your selectors return empty or your handlers don’t bind, your code may be running too early. A script placed in the head without defer can run before the target elements exist.
- Use DOM-ready — Wrap setup code in
jQuery(function($){ ... })so it waits for the DOM. - Prefer defer — Add
deferto scripts when you can, and keep all related scripts consistent. - Bind after templates render — If your HTML is injected later, bind events with delegation using
on()on a stable parent.
Fixing JavaScript $ Is Not A Function In WordPress
WordPress often triggers this error in themes and plugins because it runs jQuery in noConflict mode by default. That means $ may not point to jQuery in the global scope. On top of that, scripts can be enqueued in the wrong order, or loaded only on certain templates.
Use the WordPress-safe wrapper
In WordPress, the safest pattern is to wrap your code and pass jQuery in as $ inside the wrapper. This keeps your code tidy and avoids global collisions.
- Wrap in an IIFE — Put your code inside
(function($){ ... })(jQuery);. - Keep $ inside the wrapper — Don’t call
$outside that function on WordPress pages. - Test in the Customizer — Some themes load scripts differently in preview mode, so test both front end and Customizer.
Enqueue jQuery and set dependencies
If you enqueue scripts, set jQuery as a dependency so WordPress loads it first. If you print scripts manually in a theme file, you can end up with duplicates or wrong order.
- Declare dependencies — In
wp_enqueue_script, includearray('jquery')so WordPress handles order. - Avoid extra CDNs — Don’t load a second jQuery from a CDN unless you’ve planned for it and removed the core one.
- Check template conditions — If a script is only enqueued on one page type, confirm you aren’t using it on other pages too.
Fixing The Error In Bundlers And Modern Builds
Build tools change how scripts load. In a module world, jQuery may not be global, even when it is installed. Your code can run in strict scope where $ isn’t defined unless you import it.
Import jQuery where you use it
If you use Webpack, Vite, Parcel, or Rollup, treat jQuery like any other dependency. Import it in the file that needs it, or provide it explicitly if you have older plugins that expect a global.
- Install jQuery — Add it with your package manager so it’s in your lockfile and build.
- Import in modules — Use
import $ from 'jquery';(orimport jQuery from 'jquery';) and call it directly. - Expose globals for legacy code — If a plugin expects
window.$, assignwindow.$ = $andwindow.jQuery = $at your entry.
Watch for duplicate copies in bundles
A subtle version split can happen when one dependency brings its own jQuery copy. Then plugins attach to one copy, while your code uses another. You’ll see odd behavior like $.fn missing a plugin method.
- Search your build output — Look for “jquery” in the final bundle and check how many times it appears.
- Deduplicate packages — Use your package manager’s dedupe tools and align versions across dependencies.
- Verify plugin attachment — In the console, check
typeof $.fn.pluginNameright after the plugin loads.
Preventing “$ Is Not A Function” From Coming Back
Once you fix the immediate issue, set a few guardrails so the same mistake doesn’t return after a theme change or plugin update. This is less about adding extra code and more about making dependencies obvious.
- Prefer one source of jQuery — Pick core, a CDN, or your bundle, then stick to it across the site.
- Keep script order predictable — Avoid mixing
asyncscripts with order-dependent code. - Lint for globals — Add lint rules that warn when you use
$without importing or without a wrapper. - Log a clear check — In dev, add a small assertion like
if (typeof jQuery !== 'function') console.warn('jQuery missing');. - Retest after updates — After changing themes, plugins, or build output, run through the same two-minute diagnosis again.
If you still see the message after these fixes, go back to the console and print $ and window.jQuery again. The value you see will tell you what’s taking over. Once you know that, the path to a fix stays straightforward.
As a final sanity check, search your codebase for “$ =” and “window.$” assignments. A stray assignment can replace jQuery without you noticing. When you remove that line or localize $ inside a wrapper, the page usually settles down and your handlers start firing again.
On pages where you can’t change the load order, switch your calls from $(...) to jQuery(...). It’s a small change, and it sidesteps many collisions. That single swap also helps on WordPress pages where the global $ is intentionally reserved.
When you see javascript $ is not a function in logs from a client site, treat it like a signal, not a mystery. It points to a missing dependency, a timing issue, or a name collision. Fix that one cause, then lock it in with consistent loading rules.
It’s also normal to see the same root causes in different places. A landing page with custom code may load scripts differently from a blog post template. A checkout page may have extra scripts from payment widgets. Run the same checks per page type until the console stays quiet.
After you’ve confirmed the fix, keep an eye on your monitoring or error reporting. If the same error returns, you’ll often spot which release introduced a second jQuery file or changed a script tag attribute. Roll back that change or align the dependency list, and the issue ends.
One more time, here’s the core takeaway in plain words: javascript $ is not a function happens when $ isn’t jQuery at the moment you call it. Make jQuery load once, load it first, and keep $ from being overwritten.
