How to fix ts-node: ERR_REQUIRE_ESM in TypeScript
This error occurs when ts-node tries to load an ES module (ESM) using CommonJS require(). It happens when mixing module systems, typically when a package uses ESM exports but your tsconfig.json or runtime expects CommonJS. The fix involves aligning module systems across your configuration.
Quick fix
Fixes ERR_REQUIRE_ESM
{
"compilerOptions": {
"module": "commonjs", // This might need to change
// ... other options
}
}- Check your tsconfig.json module setting
- Update package.json with proper type field
- Configure ts-node for ESM support
- Use dynamic import() for ESM packages
{"compilerOptions": {"module": "commonjs", // This might need to change// ... other options}}ts-node: ERR_REQUIRE_ESM
What this error means
The ERR_REQUIRE_ESM error is thrown by ts-node when it attempts to use Node.js's require() function to load a module that is an ES Module (ESM). This happens because: 1. **Module System Mismatch**: Node.js has two module systems: CommonJS (using require()) and ES Modules (using import/export). They are not directly compatible. 2. **Package.json Type**: Many modern npm packages now use "type": "module" in their package.json, making them ES Modules by default. 3. **ts-node Configuration**: By default, ts-node uses CommonJS. When it encounters an ESM package, require() fails with this error. 4. **TypeScript Compiler Options**: Your tsconfig.json might be set to "module": "commonjs" while dependencies use ESM. The error typically appears when: - Importing a library that recently switched to ESM - Using TypeScript with mixed module systems - Running tests or scripts with ts-node in a project with ESM dependencies
Typical symptoms
- Error message: 'ts-node: ERR_REQUIRE_ESM' when running TypeScript files
- Scripts fail with 'Error [ERR_REQUIRE_ESM]: require() of ES Module'
- Works in development but fails in production or CI
- Error occurs after updating a dependency to a newer version
- Tests fail when using ts-node with Jest or other test runners
- Importing from node_modules works but importing local files fails
Common causes
- Package dependency uses ES Modules (has 'type': 'module' in its package.json)
- tsconfig.json has 'module': 'commonjs' but dependencies use ESM
- Using ts-node with --transpile-only flag without proper ESM configuration
- Mixing .js and .ts files with different module systems
- Node.js version doesn't fully support the ESM-CJS interop needed
- ts-node is configured for CommonJS but project uses ESM
How to fix "ts-node: ERR_REQUIRE_ESM"
First, verify your TypeScript configuration. Open tsconfig.json and check the "module" field:
{
"compilerOptions": {
"module": "commonjs", // This might need to change
// ... other options
}
}If you need to use ESM, change it to:
{
"compilerOptions": {
"module": "ESNext", // or "ES2020", "ES2022"
// ... other options
}
}For CommonJS projects, keep "module": "commonjs" but ensure proper ESM interop.
Add or modify the "type" field in your package.json:
For ESM projects:
{
"type": "module",
"scripts": {
"start": "node --loader ts-node/esm ./src/index.ts"
}
}For CommonJS projects:
{
"type": "commonjs",
"scripts": {
"start": "ts-node ./src/index.ts"
}
}If you're mixing module systems, you might need additional configuration.
If using ESM, update your ts-node configuration. Create or update tsconfig.json:
{
"ts-node": {
"esm": true,
"experimentalSpecifierResolution": "node"
},
"compilerOptions": {
"module": "ESNext"
}
}Or use command-line flags:
# For ESM
ts-node --esm --experimental-specifier-resolution=node index.ts
# Or with loader
node --loader ts-node/esm index.tsIf you need to import an ESM package in a CommonJS file, use dynamic import():
// Instead of:
// import { something } from 'esm-package';
// Use dynamic import:
const { something } = await import('esm-package');
// Or wrap in async function
async function useEsmPackage() {
const esmModule = await import('esm-package');
return esmModule.something();
}Note: Dynamic imports return Promises, so you need to handle them asynchronously.
ESM requires specific file extensions. Rename your files:
- Change .ts to .mts for ESM TypeScript files
- Change .js to .mjs for ESM JavaScript files
- Or keep extensions but ensure package.json has "type": "module"
Example structure:
src/
├── index.mts # ESM TypeScript
├── utils.mts
└── commonjs/
└── legacy.cjs # CommonJS JavaScriptUpdate imports to use full extensions:
import { helper } from './utils.mjs'; // Not './utils'If tests fail, update Jest configuration in package.json or jest.config.js:
// jest.config.js
export default {
preset: 'ts-jest/presets/default-esm',
extensionsToTreatAsEsm: ['.ts'],
moduleNameMapper: {
'^(\.{1,2}/.*)\.js$': '$1',
},
transform: {
'^.+\.tsx?$': [
'ts-jest',
{
useESM: true,
},
],
},
};Or use ts-jest with ESM:
npm install --save-dev ts-jest @types/jest
npx ts-jest config:initAdvanced notes
### Understanding Node.js Module Systems
Node.js supports two module systems:
1. CommonJS (CJS): The traditional system using require() and module.exports
2. ES Modules (ESM): The modern standard using import and export
Key differences:
- ESM is asynchronous, CJS is synchronous
- ESM has static analysis, CJS is dynamic
- ESM files need .mjs extension or "type": "module"
- CJS files need .cjs extension or "type": "commonjs"
### ts-node ESM Support
ts-node added ESM support in version 10+. Key requirements:
1. Node.js 12.20+ or 14.13+ (for stable ESM)
2. --loader ts-node/esm flag or "esm": true in tsconfig
3. Proper file extensions (.mts, .mjs)
### Common Pitfalls
1. Dual Package Hazard: When a package provides both CJS and ESM builds, Node.js might load the wrong one. Check package.json for "exports" field.
2. Circular Dependencies: ESM handles these differently than CJS. They might work in CJS but fail in ESM.
3. __dirname and __filename: These are not available in ESM. Use:
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);4. Top-level await: Only available in ESM, not CJS.
### Migration Strategy
If migrating from CJS to ESM:
1. Start with "type": "module" in package.json
2. Rename files to .mjs or keep .js with module type
3. Update all imports to use full extensions
4. Replace require() with import
5. Update module.exports to export
6. Handle dynamic imports for conditional loading
### Tools for Debugging
1. Check module type:
node -p "require('fs').readFileSync('node_modules/some-package/package.json', 'utf8')" | grep type2. Force CJS loading (not recommended for production):
node --input-type=commonjs script.js3. Use node --loader flag for custom module resolution.
Frequently asked questions
- What does "ERR_REQUIRE_ESM" mean?
- js's require() function to load a module that is an ES Module (ESM). This happens because: 1. js has two module systems: CommonJS (using require()) and ES Modules (using import/export).
- How do I fix "ERR_REQUIRE_ESM"?
- This error occurs when ts-node tries to load an ES module (ESM) using CommonJS require(). It happens when mixing module systems, typically when a package uses ESM exports but your tsconfig.json or runtime expects CommonJS. The fix involves aligning module systems across your configuration.
- What causes "ERR_REQUIRE_ESM"?
- Package dependency uses ES Modules (has 'type': 'module' in its package.json) tsconfig.json has 'module': 'commonjs' but dependencies use ESM Using ts-node with --transpile-only flag without proper ESM configuration
- Is the "ERR_REQUIRE_ESM" error serious?
- It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.