474

I'm using Webpack in my application, in which I create two entry points - bundle.js for all my JavaScript files/codes, and vendors.js for all libraries like jQuery and React. What do I do in order to use plugins which have jQuery as their dependencies and I want to have them also in vendors.js? What if those plugins have multiple dependencies?

Currently I'm trying to use this jQuery plugin here - https://github.com/mbklein/jquery-elastic. The Webpack documentation mentions providePlugin and imports-loader. I used providePlugin, but still the jQuery object is not available. Here is how my webpack.config.js looks like-

var webpack = require('webpack');
var bower_dir = __dirname + '/bower_components';
var node_dir = __dirname + '/node_modules';
var lib_dir = __dirname + '/public/js/libs';

var config = {
    addVendor: function (name, path) {
        this.resolve.alias[name] = path;
        this.module.noParse.push(new RegExp(path));
    },
    plugins: [
        new webpack.ProvidePlugin({
            $: "jquery",
            jquery: "jQuery",
            "window.jQuery": "jquery"
        }),
        new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.js', Infinity)
    ],
    entry: {
        app: ['./public/js/main.js'],
        vendors: ['react','jquery']
    },
    resolve: {
        alias: {
            'jquery': node_dir + '/jquery/dist/jquery.js',
            'jquery.elastic': lib_dir + '/jquery.elastic.source.js'
        }
    },
    output: {
        path: './public/js',
        filename: 'bundle.js'
    },
    module: {
        loaders: [
            { test: /\.js$/, loader: 'jsx-loader' },
            { test: /\.jquery.elastic.js$/, loader: 'imports-loader' }
        ]
    }
};
config.addVendor('react', bower_dir + '/react/react.min.js');
config.addVendor('jquery', node_dir + '/jquery/dist/jquery.js');
config.addVendor('jquery.elastic', lib_dir +'/jquery.elastic.source.js');

module.exports = config;

But in spite of this, it still throws an error in the browser console:

Uncaught ReferenceError: jQuery is not defined

Similarly, when I use the imports-loader, it throws an error,

require is not defined'

in this line:

var jQuery = require("jquery")

However, I could use the same plugin when I don't add it to my vendors.js file and instead required it in the normal AMD way as how I include my other JavaScript code files, like-

define(
[
    'jquery',
    'react',
    '../../common-functions',
    '../../libs/jquery.elastic.source'
],function($,React,commonFunctions){
    $("#myInput").elastic() //It works

});

But this is not what I want to do, as this would mean that jquery.elastic.source.js is bundled along with my JavaScript code in bundle.js, and I want all my jQuery plugins to be in the vendors.js bundle. So how do I go about achieving this?

Florian Grell
  • 877
  • 7
  • 17
booleanhunter
  • 5,470
  • 4
  • 13
  • 20
  • 3
    Not sure if this is your issue but you definitely need to change windows.jQuery to "window.jQuery": "jquery" . There is a typo on webpack's website where I'm assuming you got that code from. – Alex Hawkins Mar 28 '15 at 00:46
  • @AlexHawkins Oh yeah, I noticed that and fixed it. Thanks for pointing it out! – booleanhunter May 26 '15 at 07:32

11 Answers11

803

You've mixed different approaches how to include legacy vendor modules. This is how I'd tackle it:

1. Prefer unminified CommonJS/AMD over dist

Most modules link the dist version in the main field of their package.json. While this is useful for most developers, for webpack it is better to alias the src version because this way webpack is able to optimize dependencies better (e.g. when using the DedupePlugin).

// webpack.config.js

module.exports = {
    ...
    resolve: {
        alias: {
            jquery: "jquery/src/jquery"
        }
    }
};

However, in most cases the dist version works just fine as well.


2. Use the ProvidePlugin to inject implicit globals

Most legacy modules rely on the presence of specific globals, like jQuery plugins do on $ or jQuery. In this scenario you can configure webpack, to prepend var $ = require("jquery") everytime it encounters the global $ identifier.

var webpack = require("webpack");

    ...

    plugins: [
        new webpack.ProvidePlugin({
            $: "jquery",
            jQuery: "jquery"
        })
    ]

3. Use the imports-loader to configure this

Some legacy modules rely on this being the window object. This becomes a problem when the module is executed in a CommonJS context where this equals module.exports. In this case you can override this with the imports-loader.

Run npm i imports-loader --save-dev and then

module: {
    loaders: [
        {
            test: /[\/\\]node_modules[\/\\]some-module[\/\\]index\.js$/,
            loader: "imports-loader?this=>window"
        }
    ]
}

The imports-loader can also be used to manually inject variables of all kinds. But most of the time the ProvidePlugin is more useful when it comes to implicit globals.


4. Use the imports-loader to disable AMD

There are modules that support different module styles, like AMD, CommonJS and legacy. However, most of the time they first check for define and then use some quirky code to export properties. In these cases, it could help to force the CommonJS path by setting define = false.

module: {
    loaders: [
        {
            test: /[\/\\]node_modules[\/\\]some-module[\/\\]index\.js$/,
            loader: "imports-loader?define=>false"
        }
    ]
}

5. Use the script-loader to globally import scripts

If you don't care about global variables and just want legacy scripts to work, you can also use the script-loader. It executes the module in a global context, just as if you had included them via the <script> tag.


6. Use noParse to include large dists

When there is no AMD/CommonJS version of the module and you want to include the dist, you can flag this module as noParse. Then webpack will just include the module without parsing it, which can be used to improve the build time. This means that any feature requiring the AST, like the ProvidePlugin, will not work.

module: {
    noParse: [
        /[\/\\]node_modules[\/\\]angular[\/\\]angular\.js$/
    ]
}
fracz
  • 18,175
  • 16
  • 93
  • 143
Johannes Ewald
  • 16,960
  • 4
  • 40
  • 35
  • Thank you. Your answer was very descriptive. So does this mean that I can use both providePlugin and imports-loader for different files? And would it make sense to have a separate bundle only for JQuery plugins (not including JQuery)? – booleanhunter Mar 12 '15 at 12:33
  • 3
    The `ProvidePlugin` is applied on all occurrences of the given identifiers in all files. The `imports-loader` can be applied on specific files only, but you should not use both for the same variables/dependencies. – Johannes Ewald Mar 13 '15 at 16:32
  • You second question depends on your use-case, but personally I'd pack all jquery plugins + jquery itself into one bundle. – Johannes Ewald Mar 13 '15 at 16:33
  • This helped me, thanks. I figured that I was going wrong by using noParse and the ProvidePlugin together. It works now. Thanks again for the detailed answer – booleanhunter Mar 26 '15 at 07:55
  • 6
    I'm confused by this. Where is the jquery actually coming from? Locally or a CDN? Everything after 3. its unclear whether that is necessary. What are the actual steps to integrate jquery into your project using webpack. Does it bypass the version that is available via CDN? – HelpMeStackOverflowMyOnlyHope May 07 '15 at 00:17
  • 3
    Everything from a CDN is out of scope for webpack, so this refers to a local jquery installation. Jquery can just be required, there are no special steps necessary to integrate it. This question is about how to integrate jquery plugins that depend on the global `$` variable. – Johannes Ewald May 08 '15 at 09:39
  • 1
    @HelpMeStackOverflowMyOnlyHope You just need to add the aliases of your library/plugin and then add them to the entry point (in your vendors list). Then you can include them in your project files. – booleanhunter May 08 '15 at 10:48
  • @ashwinator Vendors list? What is this? – HelpMeStackOverflowMyOnlyHope May 12 '15 at 17:52
  • @HelpMeStackOverflowMyOnlyHope It's nothing but the external libraries that you wish to use in your app, for e.g jQuery, jQueryUI, underscore, bootstrap etc. you add those dependencies to the 'vendors' array (or call it whatever you want, but basically it's a different list than your JS code files) – booleanhunter May 26 '15 at 07:24
  • 1
    I had to use option 2 to get a module working. When I combine it with option 1, the jquery modules don't load in the browser with `jQuery` undefined. – Nathan Jul 24 '15 at 21:36
  • @jhnns Regarding Point 1, the src version throws an error when I use JQuery from bower instead of npm. Could you explain why? – booleanhunter Aug 27 '15 at 06:34
  • I would like to use noParse and assume jquery will be there from a CDN, but what options do I then need for libraries that do import/require jquery? i've been getting `redefinition of '$'` from jshint everywhere for those – Damon Oct 19 '15 at 15:38
  • I think `window.jQuery: "jquery"` is not working anymore, it should be `"window.jQuery": "jquery"` – MD. Jahidul Islam Feb 05 '16 at 18:23
  • 8
    Did all this, still not working; Then added: `"module": { "loaders": [ { test: require.resolve("jquery"), loader: "expose?$!expose?jQuery" },` and it worked fine. – musicformellons May 23 '16 at 13:49
  • 1
    I really think you should add some word about where `jquery` come from, the `method 2` is actually a broken one for newbie in fact. – Mithril Jul 28 '16 at 09:15
  • What do you mean with "where jquery come from"? You mean, you have to install it via npm? And why does method 2 not work for you? – Johannes Ewald Aug 02 '16 at 08:18
  • @musicformellons real nice, this resolved my issue... this should be added to the awesome article information above, cant you like provide this information to the webpack documentation since this seems to be really good explinations of how you should think,with webpack – Martea Mar 10 '17 at 13:23
  • `Prefer unminified CommonJS/AMD over dist` is this meant to all dependencies or just jquery? Cuz when I used src version for one of the plugins, didn't work. – Ean V Mar 20 '17 at 22:21
  • 7
    Do all this, to just include a jquery package ?, what a pain. I'm going back to my gulp build – Rahul Gupta Sep 04 '17 at 05:08
  • Just a note: the `jquery: "jquery/src/jquery"` alias path is not very safe. Some jQuery plugins require jQuery as a dependency and the above path matches the jQuery within the plugin/module directory which can be a different version. You can end up with few jQuery versions in your bundle and a lot of missing hair. This did the trick for us: `jquery: path.resolve(path.join(__dirname, '../../node_modules', 'jquery')) which returns something like `User/name/projects/app/node_modules/jquery` – Sams Nov 23 '17 at 16:21
  • Regarding Webpack v3: `webpack.optimize.DedupePlugin` isn't needed anymore. Remove it from your configuration. [see](https://webpack.js.org/guides/migrating/#dedupeplugin-has-been-removed) – Legends Feb 18 '18 at 20:08
  • Hi bro can you please help me out on How to update multiple bundled js files , here is my thread with 100 Bounty https://stackoverflow.com/questions/60030506/how-to-update-multiple-bundled-js-files-using-webpack/60090572#60090572 – The Dead Man Feb 06 '20 at 19:29
  • FYI it looks like "loaders" is now "rules" in webpack 4: https://stackoverflow.com/a/49376468 – dfrankow Apr 13 '21 at 22:48
  • Can you update the part with import-loader for Webpack 5? – George Valentin May 28 '21 at 18:58
94

For global access to jquery then several options exist. In my most recent webpack project, I wanted global access to jquery so I added the following to my plugins declarations:

 plugins: [
    new webpack.ProvidePlugin({
      $: "jquery",
      jQuery: "jquery"
    })
  ]

This then means that jquery is accessible from within the JavaScript source code via global references $ and jQuery.

Of course, you need to have also installed jquery via npm:

$ npm i jquery --save

For a working example of this approach please feel free to fork my app on github

Timo Tijhof
  • 9,597
  • 6
  • 31
  • 45
arcseldon
  • 29,753
  • 14
  • 104
  • 117
  • 2
    Please can you leave a comment if you downgrade a vote explaining the reason. The above information is accurate. Please see my working app at: https://github.com/arcseldon/react-babel-webpack-starter-app using the above approach. – arcseldon Dec 28 '15 at 16:49
  • I'm not the downvoter, but maybe this is because the `ProvidePlugin` solution you suggest was already proposed? – Léo Lam Dec 31 '15 at 12:24
  • @LéoLam - thanks for your comment. appreciate your point - i had deduced the answer via separate enquires, and just shared the snippet here I thought was likely most relevant to others wishing to emulate what I had done. You're right though, the official answer does cover this option. – arcseldon Dec 31 '15 at 16:39
  • 1
    It's working but I get 2 warnings: `./~/jQuery/dist/jquery.js There is another module with an equal name when case is ignored.` and the same one with `./~/jquery/dist/jquery.js` – pistou Jan 25 '16 at 10:27
  • 8
    I needed to add `'window.jQuery': 'jquery'` to that list to make the ms-signalr-client npm package load. I also put in `'window.$': 'jquery'` for good measure :) – Sammi Apr 27 '16 at 12:48
  • I dont understand where to put the `plugins:[...]` snippet. – Omar Dec 15 '17 at 17:30
  • @Omar - please see https://github.com/arcseldon/react-babel-webpack-starter-app/blob/master/webpack.config.js#L72 (linked in the answer above too) – arcseldon Dec 17 '17 at 23:02
  • I had to restart my entire server to get this working. Thanks for posting. – klewis May 23 '18 at 18:26
60

I don't know if I understand very well what you are trying to do, but I had to use jQuery plugins that required jQuery to be in the global context (window) and I put the following in my entry.js:

var $ = require('jquery');
window.jQuery = $;
window.$ = $;

The I just have to require wherever i want the jqueryplugin.min.js and window.$ is extended with the plugin as expected.

sanfilippopablo
  • 1,349
  • 1
  • 13
  • 18
  • 2
    Basically I was making the mistake of using the ProvidePlugin along with the noParse condition. Plugins like ProvidePlugin do not work if we do NoParse, as stated by in point number 6 of the answer. You can see that mistake in the code – booleanhunter May 26 '15 at 07:27
  • yeah, I've found that works more consistently across plugins than the provide plugins etc... esp if you're bringing in angular 1.x via the script-loader. – Tracker1 Feb 22 '17 at 01:35
31

I got things working nicely while exposing $ and jQuery as global variables with Webpack 3.8.1 and the following.

Install jQuery as a project dependency. You can omit @3.2.1 to install the latest version or specify another version.

npm install --save jquery@3.2.1

Install expose-loader as a development dependency if not installed already.

npm install expose-loader --save-dev

Configure Webpack to load and expose jQuery for us.

// webpack.config.js
const webpack = require('webpack')

module.exports = {
  entry: [
    // entry bits
  ],
  output: {
    // output bits
  },
  module: {
    rules: [
      // any other rules
      {
        // Exposes jQuery for use outside Webpack build
        test: require.resolve('jquery'),
        use: [{
          loader: 'expose-loader',
          options: 'jQuery'
        },{
          loader: 'expose-loader',
          options: '$'
        }]
      }
    ]
  },
  plugins: [
    // Provides jQuery for other JS bundled with Webpack
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery'
    })
  ]
}
HarlemSquirrel
  • 6,098
  • 4
  • 26
  • 30
  • 7
    You forgot to mention that you need to install `expose-loader` in order to it to work properly ```npm install expose-loader --save-dev``` – Paulo Griiettner Nov 15 '17 at 20:13
  • 4
    This worked for me . jquery : 3.3.1, expose-loader : 0.7.5, webpack : 4.20.2 – AKT Sep 28 '18 at 06:01
  • The `expose-loaded` did it for me. I didn't get an error at compile time, but in Chrome developer tools. That's solved now. – Johan Vergeer Dec 07 '19 at 20:46
  • 1
    Thank you! This worked with "jquery": "^3.4.1", and "webpack": "^4.41.5". Is it just me or getting jQuery to work with Webpack shouldn't be this ridiculous? – Carles Alcolea Feb 05 '20 at 10:17
  • 1
    For me it worked using a slightly different syntax: ` test: require.resolve("jquery"), loader: "expose-loader", options: { exposes: ["$", "jQuery"], }, }, ` – iBobb Dec 08 '20 at 13:46
20

In your webpack.config.js file add below:

 var webpack = require("webpack");
 plugins: [
    new webpack.ProvidePlugin({
        $: "jquery",
        jQuery: "jquery"
    })
 ],

Install jQuery using npm:

$ npm i jquery --save

In app.js file add below lines:

import $ from 'jquery';
window.jQuery = $;
window.$ = $;

This worked for me. :)

ashwini
  • 235
  • 2
  • 8
  • 1
    How will this act if you have for ex. app.js, and jquery-module.js, that both require jquery?, for me i get jquery13981378127,and jquery12389723198 as instances on the window? – Martea Dec 10 '19 at 08:44
  • 1
    Take care of case sensitivity on "jquery". In my webpack.config externals, I had "jquery: 'jQuery'". I had "import $ from 'jQuery'" in my script. Changing to "import $ from 'jquery'" solved my problem :) – Jono Aug 07 '20 at 05:36
18

Add this to your plugins array in webpack.config.js

new webpack.ProvidePlugin({
    'window.jQuery': 'jquery',
    'window.$': 'jquery',
})

then require jquery normally

require('jquery');

If pain persists getting other scripts to see it, try explicitly placing it in the global context via (in the entry js)

window.$ = jQuery;
John Mee
  • 44,003
  • 31
  • 133
  • 171
KhaledMohamedP
  • 3,786
  • 2
  • 21
  • 21
10

I tried some of the supplied answers but none of them seemed to work. Then I tried this:

new webpack.ProvidePlugin({
    'window.jQuery'    : 'jquery',
    'window.$'         : 'jquery',
    'jQuery'           : 'jquery',
    '$'                : 'jquery'
});

Seems to work no matter which version I'm using

Cam Tullos
  • 2,310
  • 1
  • 18
  • 16
  • +1 seems to be a misunderstanding that globals added here will automatically be set against the window, doesn't seem to work like that you need to be explicit. – James Sep 14 '17 at 21:26
  • Correct, this does not add it to the window object. It creates a window alias inside of webpack. So if you try to use `$` outside of a webpack required file, it will be undefined. – Cam Tullos Sep 18 '17 at 17:23
7

This works in webpack 3:

in the webpack.config.babel.js file:

resolve: {
    alias: {
         jquery: "jquery/src/jquery"
    },
 ....
}

And use ProvidePlugin

new webpack.ProvidePlugin({
        '$': 'jquery',
        'jQuery': 'jquery',
    })
Community
  • 1
  • 1
SharpCoder
  • 15,708
  • 34
  • 126
  • 225
  • 2
    For others that are super new at webpack (like me!) "resolve" goes in your webpack.config.js under module.exports = {} just like entry, output, plugins, module, etc.. – DavGarcia Mar 19 '18 at 16:38
  • 2
    And new webpack.ProvidePlugin goes inside the plugins array. – DavGarcia Mar 19 '18 at 16:38
3

Edit: Sometimes you want to use webpack simply as a module bundler for a simple web project - to keep your own code organized. The following solution is for those who just want an external library to work as expected inside their modules - without using a lot of time diving into webpack setups. (Edited after -1)

Quick and simple (es6) solution if you’re still struggling or want to avoid externals config / additional webpack plugin config:

<script src="cdn/jquery.js"></script>
<script src="cdn/underscore.js"></script>
<script src="etc.js"></script>
<script src="bundle.js"></script>

inside a module:

const { jQuery: $, Underscore: _, etc } = window;
frdnrdb
  • 61
  • 8
2

The best solution I've found was:

https://github.com/angular/angular-cli/issues/5139#issuecomment-283634059

Basically, you need to include a dummy variable on typings.d.ts, remove any "import * as $ from 'jquery" from your code, and then manually add a tag to jQuery script to your SPA html. This way, webpack won't be in your way, and you should be able to access the same global jQuery variable in all your scripts.

Fabricio
  • 514
  • 1
  • 6
  • 21
2

This works for me on the webpack.config.js

    new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery',
        'window.jQuery': 'jquery'
    }),

in another javascript or into HTML add:

global.jQuery = require('jquery');
JPRLCol
  • 639
  • 8
  • 23