← Back to Jest Mastery
Advanced18 min read

Jest with ESM

Configure Jest for native ECMAScript modules in Node and TypeScript projects.

ESM vs CommonJS in Jest

Modern Node projects use "type": "module" in package.json for native import/export. Jest historically assumed CommonJS—ESM support requires explicit configuration and compatible transforms.

Understand whether your source, Jest config, and dependencies are ESM, CJS, or dual-package—mixing causes intermittent import errors.

// package.json
{
  "type": "module",
  "scripts": { "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js" }
}

Jest ESM Configuration

Set extensionsToTreatAsEsm for .ts/.tsx, use ts-jest with useESM: true, or babel-jest with modules: false. NODE_OPTIONS=--experimental-vm-modules enables VM module support in Node.

jest.config must be ESM too when type module—or use jest.config.cjs for CommonJS config in ESM project.

export default {
  preset: "ts-jest/presets/default-esm",
  testEnvironment: "node",
  extensionsToTreatAsEsm: [".ts"],
  moduleNameMapper: {
    "^(\.{1,2}/.*)\.js$": "$1",
  },
};

Import and Mock Patterns

jest.unstable_mockModule replaces jest.mock for ESM dynamic mocking—it returns promise and requires await before import. Static imports hoist differently than CJS.

Import jest from @jest/globals in ESM test files for explicit API access.

  • Use .js extensions in import paths when TypeScript emits ESM
  • Dynamic import() helps break circular dependency in tests
  • Check library dual package exports for test compatibility
import { jest } from "@jest/globals";

await jest.unstable_mockModule("./api.js", () => ({
  fetchData: jest.fn(),
}));

const { fetchData } = await import("./api.js");

TypeScript and Path Aliases

Align tsconfig module with NodeNext or ESNext for ESM output. moduleNameMapper mirrors tsconfig paths for Jest resolution.

Tools like vitest offer smoother ESM DX—Jest ESM support improves each release but remains sharper edge.

moduleNameMapper: {
  "^@/(.*)$": "<rootDir>/src/$1",
}

Troubleshooting ESM Issues

SyntaxError: Cannot use import statement outside module usually means transform or extensionsToTreatAsEsm misconfiguration. ERR_REQUIRE_ESM means CJS tried to require ESM-only package—use dynamic import or pick CJS build.

Consult Jest ESM guide for version-specific flags. Pin Node LTS versions in CI matching development.

  • Isolate minimal repro when ESM config fails—one test file, one import
  • Some packages need moduleNameMapper to mock or redirect imports
  • Document working Jest ESM config in repo for onboarding

Get In Touch


Ready to discuss your next project? Drop me a message.