How to easily create JS libraries compatible with ES/AMD/UMD/CJS module systems using Nx

Problem description

Authoring libraries is always a pain if you try to maximize the number of projects that can incorporate yours.
In a perfect world, we would have to use only one module system, most probably ES modules as that is the standard in Javascript specifications, but as we might know we don’t live in a perfect world and transitioning projects takes a lot of time. So, even though now, NodeJS already has pretty good support for ES modules and all modern browsers are compatible with it a lot of projects still use CJS or UMD or even AMD modules.

In this article I’ll show you a quick and easy way on how to create a new library publishable to NPM, YARN, PNPM, whatever other package managers will exist in the future, that will target all these module systems.

In my example, I’ll use Typescript and NPM but the solution is overall agnostic of these so you could use YARN and Javascript for example.

The entire solution will also be powered up by NX, a free mono repository solution that does the entire heavy lifting for us.

Please be aware that some package names might change in the future, if that happens let me know in a comment so I can update the article.

I will be using the latest NX version available, which at the time of writing is V13, which brings a lot of improvements and a simplified process.

Prerequisites for this example

Creating the library

  • Create a folder and open a terminal in it
  • Run npx create-nx-workspace@latest LibrarySolutionName
  • Run cd LibrarySolutionName
  • Run npm i -D @nrwl/web - it will install the addon that will package our library
  • Run nx generate @nrwl/js:library --name=LibraryName --importPath=LibraryName --buildable
  • Open the newly created folder in your code editor
  • Go to packages/LibraryName/tsconfig.json and change CommonJs to EsNext or ES6.
  • Create in packages/LibraryName an empty file called babel.config.json that will contain `{}`
  • Go to packages/LibraryName/project.json and add in the targets the property

There are other interesting `options` you might consider, like:

  • assets
  • different compiler (only babel and swc are available)
  • different UMD name
  • CJS output
  • external libraries included in the bundle
  • adding dependencies

I will present you with a configuration that lists all these options

! To copy the created please move it to the packages/LibraryName/src
! To use swc as a compiler you will need to add it to the project using
npm i @swc/core

If you need to copy the assets too you can add instead

  "package": {
"executor": "@nrwl/web:rollup",
"outputs": ["{options.outputPath}"],
"options": {
"project": "packages/LibraryName/package.json",
"outputPath": "dist/packages/LibraryName",
"entryFile": "packages/LibraryName/src/index.ts",
"tsConfig": "packages/LibraryName/tsconfig.lib.json",
"compiler": "babel",
"umdName": "OtherName",
"external": ["typescript"],
"format": ["cjs", "esm", "umd"],
"assets": ["packages/LibraryName/src/"]

At this point you are pretty much done, all you need to do is run nx package LibraryName and a few seconds later you will see a dist/LibraryName folder appeared with all the files needed for publishing. If you will open the package.json you will notice a few extra properties added

These will instruct any library user from where to import each library type based on the module system they use.

Minify the bundles

If you would like to have your code minify you can take advantage of babel for that.
Run npm install babel-preset-minify --save-dev
Then in babel.config.json add "presets": ["minify"]


  • Run cd dist/packages/LibraryName
  • Run npm publish --tag=latest --access public and login

For a more advanced publishing way, you can run

nx g @nrwl/workspace:run-commands publish --project LibraryName --command 'npm publish --tag=latest --access public'

This will add a publishing executor to the packages/LibraryName/project.json that will run the publishing npm command. Then all you need to do is update the version of the package and run nx publish LibraryName and it will automatically be published.

Extra details

  • Usually the importPath is used with the following naming scheme @LibrarySolutionName/LibraryName
  • If you use it for Node do not forget to install @types/node and add them to the tsconfig.base.json and the packages/LibraryName/tsconfig.json
  • After you publish to npm your library is automatically available on unpkgr at so you can import your bundled scripts directly. Imported like this the UMD can be used in the web browser as a global object with the name of the library.

Thanks for reading! Feel free to suggest any other interesting topics to be covered in a different article.

Originally published at on December 28, 2021.




Software developer, tinkerer

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Iulian Preda

Iulian Preda

Software developer, tinkerer

More from Medium

Publish Typescript Package in NPM

React app with Typescript and custom Webpack config

Bundling a TypeScript library for Node with Rollup.JS

How global TypeScript utilities work under the hood — Partial<Type>