Question
require() vs import in Node.js: Understanding CommonJS and ES Modules
Question
I am starting a small Node.js project that connects to MongoDB, and I am having trouble importing installed packages correctly.
For example, this code throws an error saying that express has no default export:
import express from "express";
But this version works:
const express = require("express");
What is the difference between import and require() in Node.js? Also, why might import express from "express" fail while require("express") works, and how can I configure my project so imports behave correctly?
Short Answer
By the end of this page, you will understand why require() and import are not interchangeable in every Node.js project, how CommonJS and ES Modules differ, why errors like “has no default export” happen, and how to choose and configure the right module system for your project.
Concept
Node.js has used two module systems:
- CommonJS (CJS) uses
require()andmodule.exports - ES Modules (ESM) use
importandexport
They both solve the same problem: splitting code into reusable files and loading packages. But they use different syntax and have different rules.
CommonJS
CommonJS is the older module system in Node.js.
const express = require("express");
A module exports values like this:
module.exports = something;
or:
exports.myFunction = myFunction;
ES Modules
ES Modules are the standard JavaScript module system.
import express from "express";
A module exports values like this:
Mental Model
Think of modules like two different plug types.
require()is one plug type: CommonJSimportis another plug type: ES Modules
They both connect devices, but the shape of the plug is different.
Sometimes an adapter exists, so one can work with the other. Sometimes it does not fit cleanly, or you need extra configuration.
Another way to think about it:
require()says: “Load this module in the old Node.js style.”importsays: “Load this module in the standard JavaScript module style.”
If the module was packaged in a different style than your code expects, you get errors like:
has no default exportrequire is not definedCannot use import statement outside a module
So the real issue is usually not the package itself. It is a module system mismatch.
Syntax and Examples
CommonJS syntax
Importing
const express = require("express");
const fs = require("fs");
Exporting
function greet(name) {
return `Hello, ${name}`;
}
module.exports = greet;
Using it:
const greet = require("./greet");
console.log(greet("Sam"));
ES Module syntax
Default export
export default function greet(name) {
return `Hello, ${name}`;
}
Using it:
Step by Step Execution
Consider this ES Module file:
import { readFile } from "fs/promises";
async function showFile() {
const text = await readFile("message.txt", "utf8");
console.log(text);
}
showFile();
Here is what happens step by step:
- Node.js sees the
importstatement. - It checks whether the file is being treated as an ES Module.
- This usually depends on file extension or
package.jsonsettings.
- This usually depends on file extension or
- Node loads
fs/promisesand finds the named exportreadFile. - The
showFilefunction is defined. showFile()is called.readFile("message.txt", "utf8")reads the file asynchronously.awaitpauses inside the async function until the file is read.- The file contents are stored in .
Real World Use Cases
Where require() is still used
- Older Node.js applications
- Legacy Express servers
- Existing codebases that were started before ESM became common
- Quick scripts copied from older tutorials
Example:
const mongoose = require("mongoose");
const express = require("express");
Where import is commonly used
- Modern Node.js applications
- Full-stack JavaScript projects
- Frontend frameworks like React, Vue, and Svelte
- Shared code that runs in multiple JavaScript environments
Example:
import express from "express";
import mongoose from "mongoose";
Practical situations where this matters
Building an API server
You may install Express, Mongoose, and dotenv. If your project uses ESM, all your imports should follow the ESM style consistently.
Writing utility modules
A helper file may export functions for validation, formatting, or database logic. You need matching import/export syntax across files.
Real Codebase Usage
In real projects, developers usually do one of these:
Pattern 1: Stay fully in CommonJS
This is common in older Node.js applications.
const express = require("express");
const mongoose = require("mongoose");
const dotenv = require("dotenv");
dotenv.config();
Why teams do this:
- stable in older codebases
- many existing files already use it
- avoids migration work
Pattern 2: Use ES Modules consistently
This is common in newer projects.
import express from "express";
import mongoose from "mongoose";
import dotenv from "dotenv";
dotenv.config();
Why teams do this:
- standard JavaScript syntax
- consistent with frontend code
- easier to share learning across environments
Common real-project patterns
Configuration loading
Common Mistakes
1. Mixing module systems without understanding the rules
Broken example:
import express from "express";
const mongoose = require("mongoose");
This can work in some setups, but it can also cause confusion or runtime errors depending on configuration.
Better approach:
- Use one module system consistently when possible.
- If your project is CommonJS, use
require(). - If your project is ESM, use
import.
2. Forgetting to enable ES Modules in Node.js
Broken example:
import express from "express";
If your project is not configured for ESM, Node may throw an error such as:
Cannot use import statement outside a module
Fix:
- Add
"type": "module"topackage.json, or - use the
.mjsextension for ES module files
3. Confusing default exports and named exports
Comparisons
| Feature | CommonJS | ES Modules |
|---|---|---|
| Import syntax | const x = require("x") | import x from "x" |
| Export syntax | module.exports = ... / exports.x = ... | export default ... / export const x = ... |
| Original use in Node.js | Traditional Node.js standard | Newer standard supported by Node.js |
| Browser support | No native browser support | Native browser standard |
| Configuration in Node.js | Usually works by default in older setups | Often needs "type": "module" or .mjs |
Cheat Sheet
Quick rules
require()= CommonJSimport= ES Modules- Do not assume they behave exactly the same
- In Node.js, ESM usually needs
"type": "module"inpackage.jsonor.mjsfiles - In ESM, local file imports usually include the file extension
CommonJS
const express = require("express");
module.exports = myFunction;
ES Modules
import express from "express";
export default myFunction;
Named exports
export const add = (a, b) => a + b;
import { add } from "./math.js";
Default exports
FAQ
Why does require() work but import fails in Node.js?
Because your project may be using CommonJS by default, while import requires ES Module support and matching export rules.
How do I enable import in Node.js?
Add this to package.json:
{
"type": "module"
}
Then use import/export syntax consistently.
Is import better than require()?
Not automatically. import is the modern JavaScript standard, but require() is still common in older Node.js codebases.
Why does Node.js say a package has no default export?
Because you used default import syntax, but the module does not expose a default export in the way your environment expects.
Can I mix require() and in the same project?
Mini Project
Description
Build a tiny Node.js app that demonstrates both module systems by creating a reusable greeting module and loading it in an ES Module project. This helps you practice default exports, named exports, and correct Node.js configuration.
Goal
Create a small Node.js program that uses ES Modules correctly and imports functions from another file without module errors.
Requirements
- Create a
package.jsonthat enables ES Modules. - Create one module file that exports a default function and one named value.
- Create an entry file that imports both exports and prints output.
- Use valid Node.js ESM syntax, including the local file extension.
- Run the program successfully without using
require().
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.
Angular Dependency Injection: Fix "Can't Resolve All Parameters for Component" Errors
Learn why Angular shows "Can't resolve all parameters for component" and how to fix service injection issues in components.