Jest "Cannot use import statement outside a module" (ESM)
Jest hit raw ESM import syntax it never compiled to CommonJS. Usually an ESM-only dependency is skipped by the default transformIgnorePatterns, or no transform is configured for your own files.
What this error means
A suite fails to parse with "SyntaxError: Cannot use import statement outside a module," pointing at a file inside node_modules (an ESM-only package) or at your own untransformed TS/JSX source.
/app/node_modules/nanoid/index.js:1
export { nanoid } from './index.browser.js';
^^^^^^
SyntaxError: Cannot use import statement outside a moduleDiagnose it: flake, environment, or genuine failure?
Before debugging the assertion, establish whether the test is deterministic. A test that fails only in CI is usually order-dependent, time-dependent, or racing something, and fixing the assertion will not help.
# does it fail in isolation?
npx vitest run path/to/file.test.ts
# is it order dependent? run the suite in a random order twice
npx vitest run --sequence.shuffle
# is it a race? run the same file repeatedly
for i in $(seq 1 20); do npx vitest run path/to/file.test.ts || break; doneCommon causes
ESM-only dependency not transformed
Jest ignores node_modules by default. An ESM-only package (nanoid, uuid, query-string) is loaded as-is and its import breaks under CommonJS.
No transform for your own source
Without babel-jest or ts-jest configured, Jest cannot compile your own import/TSX either and throws the same SyntaxError on source files.
How to fix it
Allow-list the ESM package for transform
Carve the ESM dependency out of transformIgnorePatterns so Jest compiles it instead of skipping it.
// jest.config.js
module.exports = {
transformIgnorePatterns: ['/node_modules/(?!(nanoid|uuid|query-string)/)'],
};Configure a transform for your code
- Add
babel-jestwith@babel/preset-env(pluspreset-typescript/preset-reactas needed). - Or use
ts-jestfor TypeScript projects. - Confirm the
transformglob covers every source extension you import.
CI-only causes worth ruling out
- Runners have fewer cores than a laptop, so timing-sensitive tests that pass locally fail under contention.
- No TTY and a different locale or timezone. Snapshot tests containing formatted dates or numbers are the usual casualty; pin
TZandLANGin the job. - Parallel workers sharing a database, a port, or a temp directory. Give each worker its own namespace.
- Default timeouts calibrated on a fast machine. A cold runner is slower on first execution, especially before any cache warms.
How to prevent it
- Update
transformIgnorePatternsas ESM-only deps are added. - Standardize on
babel-jestorts-jestfor the whole repo. - Consider Vitest for ESM-first projects to avoid transform config.