Understanding and using npm exports


npm exports lets you specify how users can import or require your files. (For instructions on using require, see below).

The basic syntax looks like this:

// package.json
"name": "my-splendid-lib",
"exports": {
  ".": "./dist/main.js",
  "./components": "./dist/components.js",
  "./components/*": "./dist/components/*.js"

Let me explain what’s happening.

First, . is the path you use for people to import your library directly. If they import my-splendid-lib, they are actually importing the ./dist/main.js file.

// Actual path: /node_modules/my-splendid-lib/dist/main.js
import lib from 'my-splendid-lib'

./components (or any other name) lets people import with my-splendid-lib/components. By specifying ./components, you can determine what maps to. In this case, we mapped it to ./dist/components.js

// Actual path: /node_modules/my-splendid-lib/dist/components.js
import Components from `my-splendid-lib/components`

Finally, you use a * wildcard if you want to let users specify the paths themselves. This wildcard is often called a glob in the web development world.

In this case, if they imported my-splendid-lib/components/Accordion, Accordion will match the * wildcard. The actual file that will be imported will be ./dist/components/Accordion.js

// Actual path: /node_modules/my-splendid-lib/dist/components/Accordion.js
import Accordion from 'my-splendid-lib/components/Accordion'

That’s pretty much all you have to know if you only use ESM!

Differentiating Between CJS and ESM

For posterity:

  • CJS means Common JS
  • ESM means EcmaScript Modules

If you use Common JS, you need to require files. On the other hand, if you use ESM, you import files. There’s not much of a need to support CJS today because most people use the ESM import syntax.

// CJS
const lib = require('lib')
import lib from `lib`

If you’re writing a library that provides both CJS and ESM imports, then adding a require or import key to your exports map would do the trick.

"name": "my-splendid-lib",
"exports": {
  ".": {
    "require": './dist/index.cjs',
    "import": "./dist/index.mjs"

Now when a CJS loader requires your library, it will look under the require key and retrieve the ./dist/index.cjs file.

On the other hand, when an ESM loader imports your library, it will look for the import key and retrieve ./dist/index.mjs.

That’s it.

Not too hard, yeah? 😉

Now go and write your libraries and rock the world with your creations.

Want to become a better Frontend Developer?

Don’t worry about where to start. I’ll send you a library of articles frontend developers have found useful!

  • 60+ CSS articles
  • 90+ JavaScript articles

I’ll also send you one article every week to help you improve your FED skills crazy fast!