The JS library distribution dilemma: NPM or URL?

Is NPM always better than a simple link to distribute your JS library to third parties?

Alessandro Grosselle
Nov 12 · 7 min read

I share a few suggestions on how to develop your frontend library to ease its distribution to partners/customers, and why we chose to distribute most of our libraries using CDN instead of NPM.

Website performance is very important and the heavier a JS library is, the worst impact it will have on performance. A careful and conscientious developer will discard all libraries that greatly affect page weight.
It is not easy to convert the “very heavy” statement into a specific number of kilobytes, because it depends on the project and its performance budget.

The library architecture

A good example is Thron.js, our JS SDK: it exposes javascript methods and models which simplify the usage of THRON features such as creating multimedia content, uploads and general UX components). Thron.js has the following modules:

  • the aforementioned “bootstrap module”, which size is mostly influenced by the Polyfills needed to support IE11;
  • the “Api module”, which exposes javascript methods and models (e.g. create content from a file, get all contents from a folder…);
  • the “Component module”, it exposes all UI components;
  • the “Tracking module”, used by both the modules described above: it records the user interactions.
Thron.js application size breakdown

In the diagram above, we show the Thron.js loading flow. If for example, you want to call a method, only bootstrap, API and tracking modules will be downloaded.
All modules are loaded asynchronously with the rest of the page (the script will be executed while the page continues the parsing) to have little or no impact on page performance.
We focus on just two points when deciding if and how to break up a library:

  • Balance the number of modules and their weight; if the module is too heavy (in our case higher than 500KB not gzipped), consider splitting it into smaller modules;
  • Avoid overdoing the number of modules. Many modules of just a few KB can be a disadvantage rather than a benefit, because HTTP and network overhead will be greater than the loading time for small files.

The second point can be quite tricky to manage, consider the following example: suppose we need to implement a UI components library composed of a button, checkbox, select input, and text input. If we decide that each element is a separate module, consider what happens when your users want to build a simple web form for a webpage using all UI elements from the list, 5 different HTTP requests will be needed to load all the modules (one for bootstrap and one for each UI element). That’s a lot of connections that will be opened towards the same domain (most likely) and they will count against the browser limit: all browsers implement a limit of concurrent connections against the same domain to improve page loading speed while preserving system resources. Your increased connection count will affect page performance instead of improving it.

Distributing your library to partners and customers

We know that the most common method for publishing javascript libraries is through a package manager, such as NPM, but this last solution has a huge limit for us: it prevents our ability to hotfix it!
In an ideal world, distributing the library via NPM has only advantages because the customer/partner developers will be ready to update their version, fix the broken code (if any) and deploy the new, better, version. Unfortunately, this does not happen often: sometimes you have a customer who relied upon an external agency to develop and the whole deployment chain is just too long. Of course, we cannot (and will not) take charge of any fix, but the ability to hotfix our bugs without having to wait for the whole deployment chain to run is, sometimes, a welcome addition.

A web project, unless extremely simple, is usually built using a module bundler (for example Webpack, or Parcel).
This module bundler, in an optimized way, it bundles in one “big” file (or in more files called chunks) all your project’s javascript files, external libraries coming from NPM (folder node_modules for those who chew frontend) included.
When this happens, the library is no longer “in your hands”, but is included, encapsulated within the applications that use it.

Example of a bundled app using Webpack; all dependencies, including thron-models, are included in final chunks

Asking to update to the new version to its customers/partners, is not a trivial request: the customer/partner, must understand what are the projects impacted, understand who implemented the project (may have been done by an external consultant), understand if you can update it … in short, it is definitely a nuisance for our partners/customers that we want to avoid!

This choice of ours is also supported by many other important brands (Google, Facebook for example). Here are some examples of libraries released through CDN

There is a risk of introducing regressions when fixing the library, especially if it’s not being included in code we know. We mitigate this risk by providing versioning of the library files: as you might have noticed already, we use a version number in the library include path, this allows us to “bump” the library version any time we are aware that some regression MIGHT happen, this helps our customers/partners to retain full control about library upgrades, as it would be if they were using NPM.

The endpoint structure

https://<companyId><libName>/<libVersion>/<assetName>.js , where

  • <libName> corresponds to the library Name
  • <libVersion> corresponds to the specific library Version
Using our SDK library, version 1.0.0
Using our SDK library, version 1.0.1

Adding the library version in the URL is very important to avoid regressions; if we were not using this versioned URL, a scenario like the following one might happen:

  1. We release “lib/wonderfulLib/boot.js” library
  2. A Partner adds the library in its project and uses the library method “embed”
  3. Our team decide to improve the method “embed” modifying it. Unconsciously the changes breaks the partner implementation
  4. The library is released in production, and “lib/wonderfulLib/boot.js” points to the latest version of the library
  5. The changes are propagated in the partner’s project…
3rd party’s unwanted and uncontrolled code change propagated in production.

Two rules should always be kept in mind:

  1. Each library version must always have the corresponding URL
  2. The URL published in production must always point to the same javascript (except for important security fixes that must be communicated to your client/partner).

Is modular architecture with CDN-publishing always the best choice?

  • is the library going to be open source? If so, use NPM;
  • is the library targeted to company’s internal developers? If so, use NPM;
  • is the customer/partner willing to accept the task of managing library updates in ALL cases? If so, use NPM;
  • In any other case use CDN.

A library delivered via NPM is easier to install on a project that uses a module bundler (such ash Webpack or Parcel): we use NPM for our internal libraries or those libraries that are used just as dependencies.

To answer the initial question, the answer is NO; as usual, you should pick the right tool for the job: we love NPM and we use it where possible!

How do you manage JS library distribution? Let us know if you disagree with our approach and why you do: we’d love to discuss it.

THRON tech blog

THRON’s tech blog . THRON is a Digital Asset Management and Product Information Management SaaS that features automatic content classification (ML), real-time content rendition and real-time data analysis to perform content recommendation.

Alessandro Grosselle

Written by

Senior Software Engineer • Frontend Guild Leader @ THRON — Passionate about front end technology and everything that makes the web fast.

THRON tech blog

THRON’s tech blog . THRON is a Digital Asset Management and Product Information Management SaaS that features automatic content classification (ML), real-time content rendition and real-time data analysis to perform content recommendation.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade