DEV Community

Md Shahjalal
Md Shahjalal

Posted on

Why Do Some Packages Require `import * as …` Instead of `import …`?

When you start working with JavaScript or TypeScript, you’ll notice two different styles of importing libraries:

// Style 1
import * as moment from "moment";

// Style 2
import moment from "moment";
Enter fullscreen mode Exit fullscreen mode

At first glance, these two look similar. But depending on the project, one may work while the other gives you an error.

So why do some packages “require” import * as? And when can you use the cleaner import … style?
The answer lies in module systems and TypeScript compiler settings.


Module Systems: CommonJS vs ES Modules

There are two main ways JavaScript code is packaged:

1. CommonJS (CJS)

The original Node.js system. Uses require and module.exports.

// moment/index.js
module.exports = moment;
Enter fullscreen mode Exit fullscreen mode
// Using require
const moment = require("moment");
Enter fullscreen mode Exit fullscreen mode

2. ES Modules (ESM)

The modern JavaScript standard. Uses import and export.

// modern-style module
export default PDFDocument;

// Using import
import PDFDocument from "pdfkit";
Enter fullscreen mode Exit fullscreen mode

👉 Most older libraries like Moment.js, PDFKit, and Lodash were written in CommonJS, not ESM.


What import * as … Does

When you write:

import * as moment from "moment";
Enter fullscreen mode Exit fullscreen mode

This means:

“Import the entire module object and assign it to the variable moment.”

If the module only exports one thing (like Moment.js does), you’ll still get the whole object under that name.

✅ This always works with CommonJS libraries.
❌ It feels a little verbose.


What import … Does

When you write:

import moment from "moment";
Enter fullscreen mode Exit fullscreen mode

This means:

“Import the default export from the module.”

But here’s the catch: CommonJS libraries don’t technically have a default export.

That’s why in TypeScript you need to enable some flags in your tsconfig.json:

{
  "compilerOptions": {
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true
  }
}
Enter fullscreen mode Exit fullscreen mode

These options tell TypeScript:
👉 “Pretend that CommonJS modules export a default object so we can use import x from "y";.”


Examples in Action

Let’s see how this plays out with a few popular libraries.


Example 1: Moment.js

// Works everywhere
import * as moment from "moment";

console.log(moment().format("YYYY-MM-DD"));
Enter fullscreen mode Exit fullscreen mode
// Cleaner, but requires esModuleInterop
import moment from "moment";

console.log(moment().format("YYYY-MM-DD"));
Enter fullscreen mode Exit fullscreen mode

Example 2: PDFKit

// Namespace import
import * as PDFDocument from "pdfkit";

const doc = new PDFDocument();
doc.text("Hello, PDFKit!");
doc.end();
Enter fullscreen mode Exit fullscreen mode
// Default import (needs esModuleInterop)
import PDFDocument from "pdfkit";

const doc = new PDFDocument();
doc.text("Hello, PDFKit with default import!");
doc.end();
Enter fullscreen mode Exit fullscreen mode

Example 3: Lodash

// Namespace import
import * as _ from "lodash";

console.log(_.capitalize("hello world"));
Enter fullscreen mode Exit fullscreen mode
// Default import (needs esModuleInterop)
import _ from "lodash";

console.log(_.capitalize("hello world"));
Enter fullscreen mode Exit fullscreen mode

Why Some Projects Use * as …

  • They have strict TypeScript settings (esModuleInterop: false).
  • They want to stay compatible with pure CommonJS modules.
  • Older codebases often stick with this style.

Why Some Projects Use Default Imports

  • They enabled esModuleInterop: true.
  • They prefer the cleaner syntax:
import moment from "moment";
import PDFDocument from "pdfkit";
import _ from "lodash";
Enter fullscreen mode Exit fullscreen mode
  • It matches modern ESM conventions.

Which One Should You Use?

  • For new projects: enable esModuleInterop in tsconfig.json and use import x from "package".
  • For legacy or strict projects: use import * as x from "package".
  • For modern libraries (like Luxon, Axios, etc.): always use import x from "package" — they’re written as ES Modules.

The difference isn’t about Moment.js, PDFKit, or Lodash themselves.
It’s about how TypeScript bridges the gap between CommonJS and ES Modules.

  • import * as X → Import the entire module object (safe for all CommonJS packages).
  • import X → Import the default export (only works if you enable esModuleInterop).

👉 So, if a package seems to “require” * as, it’s not the package’s fault — it’s just your project’s module configuration.


Top comments (0)