jQuery plugins in webpack without jQuery
In webpack, some dependencies are best served vanilla
Have you seen this console error while trying to adopt webpack?
Uncaught TypeError: $(...).myJqueryPlugin is not a function
Assuming you installed it correctly, there could be a few explanations. In this post, we'll look one possible reason: with webpack, your jQuery plugin might not need jQuery.
Subscribe to my newsletter, Joy of Rails, to get notified about new content.
Example: the Flickity plugin
To take advantage of this distinction, more plugins are being written without the assumption of jQuery as a dependency, but with the ability to use a plugin to support (what's becoming) the legacy pattern.
Here's an example. The popular jQuery plugin Flickity makes it easy to construct "responsive, flickable carousels" as follows:
$('.main-carousel').flickity({
// options
cellAlign: 'left',
contain: true,
})
And that works fine. Except, Flickity
is not really a just a jQuery plugin. More on that later.
Let's say we have a Rails application that we've already set up to run webpack(er) (or just plain webpack) for compiling JavaScript source files for the browser. To add Flickity, we installed it via yarn:
$ yarn add flickity
In our module-based webpack build, the usage is instead ideally:
// app/javascript/src/carousel.js
import Flickity from 'flickity'
const flickity = new Flickity('.main-carousel', {
// options
cellAlign: 'left',
contain: true,
})
For this plugin, jQuery is not required. How do we know that though?
Package perusal
The first place to check is the documentation. As is the case for Flickty, there is a great documentation site with examples for both jQuery and vanilla JS initialization.
Here's a screenshot from the Flickity homepage:
This is a great hint that we can import the Flickity
constructor without requiring jQuery on the page or in our build.
If that's still not enough, the next place to look is the library's package.json main
property. This property describes the entry point to the package, meaning this is the file that's loaded when require
or import
statement is used to access the module.
Here's an excerpt from Flickity's package.json
:
{
"name": "flickity",
"main": "js/index.js"
// ...
}
This is saying thath the file path/to/flickity/js/index.js
is the entry point. On my machine from the root of my project, I can open that file at ./node_modules/flickity/js/index.js
or on GitHub (source) (slightly modified for this article).
( function( window, factory ) {
if ( typeof define == 'function' && define.amd ) {
// AMD
define( [ './flickity', './drag', './prev-next-button', './page-dots', './player', './add-remove-cell', './lazyload' ], factory );
} else if ( typeof module == 'object' && module.exports ) {
// CommonJS
module.exports = factory( require('./flickity'), require('./drag'), require('./prev-next-button'), require('./page-dots'), require('./player'), require('./add-remove-cell'), require('./lazyload'));
}
})( window, function factory( Flickity ) {
return Flickity;
});
We can see this exports a module, either through the define
function, for runtimes that support Asynchronous Module Definition (AMD) format, or the require
function, for runtimes that support CommonJS format. webpack supports both.
jQuery not required
Note also that this file does not require the 'jquery'
package. An interesting consequence of this is that it's not even possible to use Flickity with jQuery (at least via the NPM package alone). This recently came up in a GitHub issue for the Webpacker project.
In other words, the following code with webpack:
// app/javascript/src/carousel.js
import 'jquery'
import 'flickity'
$('.main-carousel').flickity({
// options
cellAlign: 'left',
contain: true,
})
would result in this error:
Uncaught TypeError: $(...).flickity is not a function
This project can produce a separate distribution from the package available via NPM. It takes the form of a file, here called flickity.pkgd.js
that is intended for the browser via a script tag. This file contains a "jQuery-fied" version of the underlying vanilla JS constructor which makes it possible to use with jQuery in the browser. For more details, see the jquery-bridget project.
A similar approach exists in other plugins including:
The main takeaway here: if the library can be initialized without jQuery, there's not much reason to use jQuery for that plugin in the first place.
Conclusion
Unfortunately, jQuery plugins come in so many different flavors so not all of them can be used without jQuery. In future posts, I'll discuss other techniques you can use to make jQuery work with webpack.
If you find yourself in this position, make sure to understand the capability of the plugins you're using. Ask "do I have to use this plugin with jQuery?", consult the documentation and/or browse the source code as described in the article. If you're lucky, you may find they can work in either context such that you might not need jQuery at all.